aws-cloudformation / cfn-language-discussion

Language discussions for CloudFormation template language
https://aws.amazon.com/cloudformation/
Apache License 2.0
142 stars 13 forks source link

Support YAML anchors/aliases in CFN yaml templates #49

Open x6j8x opened 3 years ago

x6j8x commented 3 years ago

Doesn't really fit the issue template, but it would be great if CFN YAML templates would allow to use YAML anchors & aliases.

This way one could simply set an anchor and then refer back to it in other places of the template.

Example (non-cfn - only to demonstrate anchors/aliases)

a: &numbers
- 1 
- 2
- 3

b: *numbers

is the same as

a:
- 1 
- 2
- 3

b:
- 1 
- 2
- 3

Advantages:

kidbrax commented 3 years ago

This would be huge. Not sure why it's not available already.

iainelder commented 3 years ago

My use case: I have two stacksets in a parent stack. The stacksets have various configuration properties that should be the same: PermissionModel, AutoDeployment, OperationPreferences, CallAs, Capabilities.

A YAML anchor would be a very simple way to keep this configuration DRY ("don't repeat yourself").

Without an anchor, I think my deduplication options are to use a substack to encapsulate the the common configuration or to generate the deployed template from another source.

In my case I don't want to add that complexity, I really just want to have a single template file (even the stackset template body is defined inline with TemplateBody!).

So for now I have to live with the duplicatation with a comment to the effect of "These settings should be the same for each stack". :smile:

unfor19 commented 2 years ago

@x6j8x I had the same issue with many services that disallow YAML anchors. I've created the project yarser, a CLI that parses a YAML file that contains anchors to a plain-text YAML file.

Here's how it works:

yarser-demo

It is written in Go and based on yq, so the parsing process is quite fast, especially if you use the --watch mode, where it automatically parses the file on save (like in the demo above).

I know it's not the perfect solution, and it would be better if CloudFormation would support YAML anchors, but until then, you can use yarser.

kidbrax commented 2 years ago

any update on this?

krystof-k commented 2 years ago

Maybe a useful note for someone: when using aws cloudformation package via AWS CLI, it processes the YAML and anchors works just fine.

E.g.:

Resources:
  TaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      …
      ContainerDefinitions:
        - &container
          Name: hello-world
          Image: hello-world
        - <<: *container
          Name: ciao-world
kidbrax commented 2 years ago

Thanks @krystof-k! I'll give it a shot. This could be super helpful!

lejiati commented 2 years ago

@x6j8x Thank you very much for your feedback! Since this repository is focused on resource coverage, I'm transferring this issue over to a new GitHub repository dedicated to CloudFormation template language issues.

Lucas3oo commented 1 year ago

+1

manchicken commented 1 year ago

+1

davidjb commented 1 year ago

Having equivalent support within AWS Step Function state machine definitions as well would be great help to reduce and centralise repetitive configuration. In the config I've tried to deploy just now, using an alias results in SCHEMA_VALIDATION_FAILED (InvalidDefinition) errors; anchors seem okay to remain in the YAML but serve no purpose without aliases to reference them.

yvele commented 1 month ago

Maybe a useful note for someone: when using aws cloudformation package via AWS CLI, it processes the YAML and anchors works just fine.

@krystof-k you mean that we have to use aws cloudformation package with --use-json option:

aws cloudformation package \
  --template template.yaml \
  --s3-bucket somebucketforcloudformation \
  --output-template-file template.json \
  --use-json

When converting from YAML to JSON, all anchors and aliases will be resolved.

More info on this workaround: https://medium.com/@rhgvandenheuvel/cloudformation-coding-using-yaml-9127025813bb

But I would prefer this being natively handled by CloudFormation as YAML is more readable than JSON.

krystof-k commented 1 month ago

@yvele Yes, but you don't have to convert it JSON, it works with YAML too:

aws cloudformation package \
  --template-file template.yaml \
  --output-template-file packaged-template.yaml \
  --s3-bucket bucket
yvele commented 1 month ago

@krystof-k yeah you are right 👍

I'm facing errors because I want to use anchors/aliases within a StackSet template body: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cloudformation-stackset.html#cfn-cloudformation-stackset-templatebody

StackSet:
  Type: AWS::CloudFormation::StackSet
  Properties:
    TemplateBody: |
      Foo: &anchor
        - a
        - b
      Bar: *anchor

and obviously TemplateBody is a string and anchor/aliases will not be converted within 🤔 I'm a bit stuck

I tried the following:

Transform: AWS::LanguageExtensions
...

StackSet:
  Type: AWS::CloudFormation::StackSet
  Properties:
    TemplateBody: !ToJsonString
      Foo: &anchor
        - a
        - b
      Bar: *anchor

It's kind of working.. but the problem with ToJsonString is that my object will have GetAtt, etc. being resolved outside the StackSet (resolved from the root document):

Transform: AWS::LanguageExtensions
...

StackSet:
  Type: AWS::CloudFormation::StackSet
  Properties:
    TemplateBody: !ToJsonString
      SomeResource:
        Type: AWS::SomeResource
      Foo: $anchor
        - a
        - !GetAtt SomeResource.Arn # This will try to find SomeResource in the root document outside the StackSet
      Bar: *anchor

Template error: instance of Fn::GetAtt references undefined resource SomeResource

I can't find any workaround to resolve anchors/aliases within a StackSet 🤔

krystof-k commented 1 month ago

@yvele You are doing it wrong, YAML anchors don't work like that. Try to do it like in my example above. you should use & instead of $.

And I think the !ToJsonString function should not matter, because it gets evaluated after the YAML, but I haven't tried that. With the multiline string it won't work.

yvele commented 1 month ago

@yvele You are doing it wrong, ~YAML anchors don't work like that. Try to do it like in my example above.~ you should use & instead of $.

Oops my bad that was a typo only within my response to the thread, I am using & in my real code.

And I think the !ToJsonString function should not matter, because it gets evaluated after the YAML, but I haven't tried that. With the multiline string it won't work.

Hum really with !ToJsonString I get this error:

Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state: For expression "Status" we matched expected path: "FAILED" Status: FAILED. Reason: Template error: instance of Fn::GetAtt references undefined resource SomeResource

I don't have the error when I use YAML as plain text but then the StackSet fails during deployment with this error:

Resource handler returned message: "Template error: YAML aliases are not allowed in CloudFormation templates (Service: CloudFormation, Status Code: 400, Request ID: XXX)" (RequestToken: XXX, HandlerErrorCode: GeneralServiceException)

image

krystof-k commented 1 month ago

@yvele Yeah, because you are trying to resolve SomeResource inside !ToJsonString, that can't work, but that is not related to the YAML anchors.