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鈥檝e been convinced that they鈥檙e actually separate issues, thought they鈥檇 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 }}}
Most helpful comment
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:
They went further, because they wanted to do this with
Fn::ImportValuebut they said it wasn't working in parameter defaults. So they were clever and created resources they could!Ref, using throwaway SSM parameters:This would be significantly improved with a section dedicated to this purpose. I think of them as constants rather than variables: