> proposes solution that involves YAML
Am I crazy, ignorant, or is YAML the most tedious and error-prone format edit?
> proposes solution that involves YAML
Am I crazy, ignorant, or is YAML the most tedious and error-prone format edit?
Projects like Pulumi and CDK seem to be much better approaches, but don't seem to have much traction compared to TF, Cloudformation, etc.
Then some tools came out which let you use YAML or JSON to describe the desired state of the world, and some tool would diff that against the current state of the world to determine which resources to create, which to delete, and which to update. People started to conflate "YAML" with this sort of diffing tool and they conflated imperative programming languages with the legacy imperative scripting approach. Early infra-as-code vendors capitalized on the YAML=good/programs=bad myth and re-emphasized it by showing cute toy examples and passing off the simplicity as an effect of the YAML/etc rather than the inherent simplicity of the example.
Unfortunately, pure YAML/HCL/etc doesn't scale--you end up needing to reference resources from other resources (and/or attributes on those resources), and you end up needing to DRY up many repetitious blocks and so on. What you naturally want is to generate those static YAML configs from a programming language; however, IaC vendors had committed themselves to the "YAML = simple" brand so instead they started building half-assed programming language features into their YAML dialect (effectively using YAML to represent abstract syntax trees for the world's crappiest programming languages). Terraform and CloudFormation fall into this bucket.
I guess Helm looked at the landscape and decided it wasn't easy enough to generate syntactically invalid YAML blobs, and consequently decided to use text templates.
Eventually the industry caught on to the scam and demanded programming languages back, so we got CDKs which misunderstand the assignment in a different way. Rather than emitting YAML/etc that can be passed into a diff engine, you get some weird bindings that calls (and is called from) a node process (I can't tell what this node process actually does even after reading the docs).
Troposphere emits YAML.
You can diff the outputs if you wish, or did I misunderstand your last paragraph.
These are two examples I found online. The first is more complicated but it doesn't do any JavaScript IPC, no inheritance, no mutation, etc. You write it just like you want to write YAML, and it straightforwardly emits YAML (rather than the CDK version which is more opaque/magical). I prefer this:
def main():
bucket_name_parameter = ParameterString(Description="The name of the bucket")
key_arn_parameter = ParameterString(
Description="The ARN of the KMS key used to encrypt the bucket",
)
bucket = Bucket(BucketName=bucket_name_parameter)
t = Template(
description="S3 Bucket Template",
parameters={
"BucketName": bucket_name_parameter,
"KMSKeyARN": key_arn_parameter,
},
resources={
"Bucket": bucket,
"BucketPolicy": ManagedPolicy(
PolicyDocument={
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowFullAccessToBucket",
"Action": "s3:*",
"Effect": "Allow",
"Resource": Sub(
f"${{BucketARN}}/*", BucketARN=bucket.GetArn()
),
},
{
"Sid": "AllowUseOfTheKey",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
],
"Resource": key_arn_parameter,
},
{
"Sid": "AllowAttachmentOfPersistentResources",
"Effect": "Allow",
"Action": [
"kms:CreateGrant",
"kms:ListGrants",
"kms:RevokeGrant",
],
"Resource": key_arn_parameter,
"Condition": {"Bool": {"kms:GrantIsForAWSResource": True}},
},
],
},
),
},
)
print(json.dumps(t.template_to_cloudformation(), indent=4))
Rather than this: class S3Stack(Stack):
def __init__(self, app: App, id: str) -> None:
super().__init__(app, id)
self.access_point = f"arn:aws:s3:{Aws.REGION}:{Aws.ACCOUNT_ID}:accesspoint/" \
f"{S3_ACCESS_POINT_NAME}"
# Set up a bucket
bucket = s3.Bucket(
self,
"example-bucket",
access_control=s3.BucketAccessControl.BUCKET_OWNER_FULL_CONTROL,
encryption=s3.BucketEncryption.S3_MANAGED,
block_public_access=s3.BlockPublicAccess.BLOCK_ALL,
)
# Delegating access control to access points
# https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-points-policies.html
bucket.add_to_resource_policy(
iam.PolicyStatement(
actions=["*"],
principals=[iam.AnyPrincipal()],
resources=[
bucket.bucket_arn,
bucket.arn_for_objects('*')
],
conditions={
"StringEquals":
{
"s3:DataAccessPointAccount": f"{Aws.ACCOUNT_ID}"
}
}
),
)
Note: Compared to Troposphere, the bindings in the first example are completely generated from a spec published by AWS, so they never fall behind. They're also type-annotated so you can use those bindings with type safety. Sadly, the project has been abandoned because it's CloudFormation-specific and the world has moved away from CloudFormation.You’re in for a shock when you realise all they do is emit YAML
I think it's a pleasant middle ground between config and code, it's definitely code, but limited.
I lile python, I like significant whitespace, I hate how YAML does it.
Python, Go, even Scratch (for a 100% GUX)… And among the many pros, error handling is enough to warrant the change from markup.
But IMHO here's the one barrier: people need to stop thinking of an Information System (IS) as a collection of machines, and move one level of abstraction above to consider said IS as the unit, and machines as subs of sort (subsystems, components, modules, call it what you want). Conceptually, your "main" space is the IS, everything else is modules down the namespace hierarchy. We need to think of machines as we used to think of programs, and think of IS's as we used to think of machines. In the UNIX philosophy, any application is a collection of programs, just as an IS-as-code is a collection of machines-as-software-modules (basically feature libraries for "main" to use).
When that paradigm comes, you'll be able to simply "import" some subsystem (say Elastic Search, whatever machines in the IS) and add that (as a class, method, whatever) to all your existing objects, because the concept of "interfaces" (quite literally the basis of interoperability, dating back forever in computing) will be _native_ to your IS model in a Turing-complete paradigm.
Not sure my wording made sense to non-programmers (cue: "quotes" are technical terms, not general meaning), but I'm too lazy to write a layman expose.
The problem is more with the software that uses it, and it using YAML is just a high quality proxy for the signal you want.
YAML is a bit error prone, but it's those tools that have an incredibly complex format.
But it can be tricky to read other people's YAML files on GitHub. It's easy to miss subtle mistakes.
Though better than when Jinja is used on YAML.
Do you edit Python Pickle files by hand?
PS: I forgot that I wrote a cloud init file merely hours ago. Yuck.