cfn-language-discussion
cfn-language-discussion copied to clipboard
Template Variables
Sorry, this doesn't really fit the issue template. It would be awesome if the template had a section called something like "Variables" where I could alias a large !Sub or !Join to a smaller token.
AWSTemplateFormatVersion: 2010-09-09 # TODO Update
Parameters:
DOMAIN:
Type: String
Description: "URL domain to use"
STAGE:
Type: String
Description: "URL stage to use"
Variables:
HostedZone:
Type: String
Value: !Sub "${STAGE}.${DOMAIN}"
Resources:
MyAPI:
Type: AWS::ApiGateway::RestApi
APIDomainName:
Type: AWS::ApiGateway::DomainName
Properties:
CertificateArn:
Fn::ImportValue: "ACMCertArn"
DomainName: !Ref HostedZone
APIBasePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
DomainName: !Ref APIDomainName
RestApiId: !Ref MyAPI
Stage: Prod
APIDomain:
Type: AWS::Route53::RecordSetGroup
Properties:
HostedZoneName: !Sub "${HostedZone}."
RecordSets:
- Name: !Ref HostedZone
Type: A
AliasTarget:
DNSName: !GetAtt APIDomainName.DistributionDomainName
HostedZoneId: !GetAtt APIDomainName.DistributionHostedZoneId
This particular example has the variable inputs coming only from the Parameters, but it should allow me to ref elements created inside the template as long as it doesn't create a circular dependency.
Agreed !
Closing this because @steven-cuthill-otm 's issue is a much better (more generic) version of this request.
https://github.com/aws-cloudformation/aws-cloudformation-coverage-roadmap/issues/86
I’ve been convinced that they’re actually separate issues, thought they’d compliment each other nicely.
This one feature would make CloudFormation significantly easier to use.
A developer was showing me a template today. They had around 15 template parameters, none of which were intended to be provided by the user of the template. Instead, they were using parameters to declare the set of "constants" the template relied on.
It looked like this:
Parameters:
EventSchedule:
Type: String
Default: "cron(0 12 * * ? *)"
FooValue:
Type: AWS::SSM::Parameter::Value<String>
Default: /sharedprefix/foo
BarValue:
Type: AWS::SSM::Parameter::Value<String>
Default: /sharedprefix/bar
They went further, because they wanted to do this with Fn::ImportValue
but they said it wasn't working in parameter defaults. So they were clever and created resources they could !Ref
, using throwaway SSM parameters:
Resources:
MyValue:
Type: AWS::SSM::Parameter
Properties:
Type: String
Value: !ImportValue MyValue
This would be significantly improved with a section dedicated to this purpose. I think of them as constants rather than variables:
Constants:
EventSchedule: "cron(0 12 * * ? *)"
ParamPrefix: "/sharedprefix"
FooValue:
Type: AWS::SSM::Parameter::Value<String>
Value: !Sub '${ParamPrefix}/foo'
BarValue:
Type: AWS::SSM::Parameter::Value<String>
Value: !Sub '${ParamPrefix}/bar
MyValue:
Type: String
Value: !ImportValue MyValue
@benkehoe I've done similar things, including adding a AllowedValues to prevent modification:
Parameters:
EventSchedule:
Type: String
Default: "cron(0 12 * * ? *)"
AllowedValues: [ "cron(0 12 * * ? *)" ]
There's also a problem with the "constants as parameters with default values": changing the default for a parameter in a stack update does not change the value of that parameter, even if it its current value was provided by the default. Adding support for a separate section with different semantics would allow for updates to these values in stack updates.
I am right now copying and pasting a triple nested collection of macros to sanitize a parameter input (sometimes I want plain english, sometimes camelcase, and sometimes S3 suitable version of the same parameter). Being able to define this collection of nested macros once and reference elsewhere would save me copy / pasting and make the code more readable.
I like this idea of Constants, it would greatly improved my templates (note that ssm parameters are automatically created at the init of the project).
Parameters:
ProjectName:
Description: (Required) Project name (only ascii chars).
Type: String
AllowedPattern: '[\x20-\x7E]*'
MinLength: 1
ConstraintDescription: Only ASCII characters are allowed.
Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Metadata:
cfn-lint: {config: {ignore_checks: [E1019]}}
Properties:
Scheme: internal
Name: !Sub
- '${ProjectLower}-lb-${StackId}'
- ProjectLower: {'Fn::Transform': {'Name': String, 'Parameters': {'InputString': !Ref ProjectName, 'Operation': Lower }}}
StackId: !Select [0, !Split [-, !Select [2, !Split [/, !Ref AWS::StackId ]]]]
Subnets:
- !Sub
- '{{resolve:ssm:/abc/landing-zone/${RootAccountType}/sharedvpc-${AWS::Region}/subnets/${SubnetScope}/subnet-1}}'
- RootAccountType: {'Fn::ImportValue': 'root-account-type'}
- !Sub
- '{{resolve:ssm:/abc/landing-zone/${RootAccountType}/sharedvpc-${AWS::Region}/subnets/${SubnetScope}/subnet-2}}'
- RootAccountType: {'Fn::ImportValue': 'root-account-type'}
- !Sub
- '{{resolve:ssm:/abc/landing-zone/${RootAccountType}/sharedvpc-${AWS::Region}/subnets/${SubnetScope}/subnet-3}}'
- RootAccountType: {'Fn::ImportValue': 'root-account-type'}
SecurityGroups:
- !Sub
- '{{resolve:ssm:/abc/project/${ProjectLower}/security-group/sg-app-front}}'
- ProjectLower: {'Fn::Transform': {'Name': String, 'Parameters': {'InputString': !Ref ProjectName, 'Operation': Lower }}}
Tags:
- Key: myTagA
Value: {'Fn::Transform': {'Name': String, 'Parameters': {'InputString': !Ref ProjectName, 'Operation': Upper }}}
- Key: myTagB
Value: !Sub
- '{{resolve:ssm:/abc/project/${ProjectLower}/map/app}}'
- ProjectLower: {'Fn::Transform': {'Name': String, 'Parameters': {'InputString': !Ref ProjectName, 'Operation': Lower }}}
- Key: myTagC
Value: !Sub
- '{{resolve:ssm:/abc/project/${ProjectLower}/map/value}}'
- ProjectLower: {'Fn::Transform': {'Name': String, 'Parameters': {'InputString': !Ref ProjectName, 'Operation': Lower }}}
@rhboyd 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.
This is hugely beneficial. Big +1! When can we get this?
The Value
key should support 3 types of notation: string, list, and map. Also, Type
should be optional.
Variables:
MyTextVariable:
Value: !Sub "${STAGE}.${DOMAIN}"
MyListVariable:
Value:
- First Value
- Second Value
MyMapVariable:
Value:
First: Value
Second: Other Value
I'd say that the value should be able to be any valid JSON type, and that putting, say, Type: Number
on it should allow for conversion of a string containing a number into a proper number.
I also think there's a good argument that the type should also be able to be any CloudFormation resource type, where the value is a valid identifier for that type, and the resulting variable (after a call to the type's read handler) can be Ref
'd and GetAtt
'd like a resource. (I think this should be true for template parameters as well).
As far as I can tell, the only literal type in CloudFormation is string
. The various parameter types, such as Number
are just input validators (as well as transforms, but that's out-of-scope anyway). These would not be appropriate for variables. For example, it wouldn't make sense to validate that a resource attribute is a number no larger than 10. How would such an assertion even work in practice?
Ref
Implementing Ref
is pretty simple as it only requires in-lining with infinite recursion detection. It also needs support for Sub
.
BEFORE:
Variables:
MyMessage: !Sub "Hello ${Who}"
Who: World
Resources:
MyTopic:
Type: AWS::SNS::Topic
Properties:
DisplayName: !Ref MyMessage
AFTER:
Resources:
MyTopic:
Type: AWS::SNS::Topic
Properties:
DisplayName: !Sub
- "Hello ${Who}"
- Who: World
GetAtt
GetAtt
is tricky, because it's only valid on resources. I'm not even sure how you would declare such a variable? You can't use Ref
in the declaration since that has its own meaning.
Example, how would you declare that MyTopicResource
is the actual resource and not its ARN (as returned by Ref
)?
Variables:
MyTopicResource: !Ref MyTopic
Resources:
MyTopic:
Type: AWS::SNS::Topic
Apologies but I have looked around but couldn’t find if there’s any documentation how all of the CF repos hang together?
Looking for a primer that would allow me to figure out what repo/s would for example potentially need modification to support this feature.
Anyone know?
Apologies but I have looked around but couldn’t find if there’s any documentation how all of the CF repos hang together?
Looking for a primer that would allow me to figure out what repo/s would for example potentially need modification to support this feature.
Anyone know?
The backend code of the CloudFormation service is private/closed source. AWS employees would need to make this change.
That’s a shame. Thanks for replying.
AWS pseudo-parameters exist, and this issue reads to me as though user-defined pseudo-parameters would be sufficient, provided those pseudo-parameters allow for CFN template functions (!GetAtt
, !Sub
, etc.) to be used. I agree that would be helpful in reducing template size and making complicated-looking statements easy to read.
@benkehoe I've done similar things, including adding a AllowedValues to prevent modification:
Parameters: EventSchedule: Type: String Default: "cron(0 12 * * ? *)" AllowedValues: [ "cron(0 12 * * ? *)" ]
That's a clever idea.
So it's been 5 years since this was requested and has a decent amount of thumbs up.
What's stopping AWS from implementing it after all this time and more importantly... how can we get some focus on it?
I would suggest CDK, at this point cloudformation is the equivalent of assembly language.