CloudFormation Pass All Parameters from Root Stack to Nested Stack - amazon-web-services

Is there a way to pass every parameter that a root stack receives to its nested stacks? I can pass one parameter at a time just fine, but I'd like to just pass them all at once.

Here is sample template to give you an idea.
Master.yaml:
Resources:
Cloudspan:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
LambdaName: Cloudspan
BucketName: <BucketName>
S3KeyName: <S3KeyName>
FunctionName: <FunctionName>
TemplateURL: <TemplateURL>
Alignment:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
LambdaName: Alignment
BucketName: <BucketName>
S3KeyName: <S3KeyName>
FunctionName: <FunctionName>
TemplateURL: <TemplateURL>
Lambda-child.yaml:
Parameters:
LambdaName:
Type: String
BucketName:
Type: String
S3KeyName:
Type: String
FunctionName:
Type: String
Resources:
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Handler: !Sub '${LambdaName}-{FunctionName}.Handler'
Role:
Fn::GetAtt: ['LambdaExecutionRole', Arn ]
Code:
S3Bucket: !Sub '${LambdaName}{BucketName}'
S3Key: !Sub '${LambdaName}{S3KeyName}'
Runtime: "python3.6"

There is no way (yet) to pass every parameter at once from the root stack to the nested stack. If you want to pass every parameter, you have to do it one by one as the template given in Sudharsan Sivasankaran's answer.

Fn::Import
You can "export" your parameters as outputs from one stack and then any stack will be able to access those values across your entire account. This might be more open than you are looking for but if your end goal is to get stacks to share variables then exporting outputs and referencing them with Fn::Import accomplishes that.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html

Related

Importing s3 Bucket in Cloudformation template

In a Cloudformation template, I define two S3 Buckets.
Bucket1:
Type: AWS::S3::Bucket
Properties:
...
Bucket2:
Type: AWS::S3::Bucket
Properties:
...
Outputs:
Bucket1:
Description: S3 Bucket
Value: !Ref Bucket1
Export:
Name: !Sub "${AWS::StackName}:Bucket1"
Bucket2:
Description: S3 Bucket
Value: !Ref Bucket2
Export:
Name: !Sub "${AWS::StackName}:Bucket2"
I use these exported buckets in two different cloudformation templates.
Template 1
Parameters:
LoaderCodeBucket:
Type: String
Resources:
MyLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket:
Fn::ImportValue:
!Sub "${LoaderCodeBucket}:Bucket1"
Template 2
Parameters:
ProcessorCodeBucket:
Type: String
Resources:
MyOtherLambdaFunction:
Type: AWS::Lambda::Function
Properties:
Code:
S3Bucket:
Fn::ImportValue:
!Sub "${ProcessorCodeBucket}:Bucket2"
Template 1 passes aws cloudformation validate-template --template-body ... while Template 2 fails due to
Template error: the attribute in Fn::ImportValue must not depend on any resources, imported values, or Fn::GetAZs.
The only difference is the lambda function in template 2 is used in an aws analytics application that is also defined in template 2.
I know for sure it's the S3 Bucket causing issues because when I remove that section of code, it passes the validation check.
I have been using this site to try to debug this issue, but none of the questions seem to answer this particular issue.
This is in same region/same account.
My question is:
Why is this particular section of code (template 2) throwing a template error when template 1 passes with no error?
This is a working example.
Template 1:
AWSTemplateFormatVersion: "2010-09-09"
Description: "Test"
Resources:
MyBucketOne:
Type: "AWS::S3::Bucket"
Properties:
BucketName: bucket-one-12341234
MyBucketTwo:
Type: "AWS::S3::Bucket"
Properties:
BucketName: bucket-two-12341234
Outputs:
MyBucketOneOutput:
Description: "Bucket Name of BucketOne"
Value: !Ref MyBucketOne
Export:
Name: !Sub "${AWS::StackName}-BucketOne"
MyBucketTwoOutput:
Description: "Bucket Name of BucketTwo"
Value: !Ref MyBucketTwo
Export:
Name: !Sub "${AWS::StackName}-BucketTwo"
Template 2: we can import it as !ImportValue my-s3-BucketOne
AWSTemplateFormatVersion: "2010-09-09"
Description: "Test"
Resources:
MyLambda:
Type: "AWS::Lambda::Function"
Properties:
Handler: index.handler
Runtime: nodejs12.x
FunctionName: "test-s3-import"
Code:
S3Bucket: !ImportValue my-s3-BucketOne
S3Key: "index.zip"
Description: "Test Lambda"
MemorySize: 128
Timeout: 60
Role: test-role-arn
If you do want to use from Parameter, it will be Fn::ImportValue: !Sub ${BucketExportNamePrefix}-BucketOne
AWSTemplateFormatVersion: "2010-09-09"
Description: "Test"
Parameters:
BucketExportNamePrefix:
Type: String
Default: "my-s3"
Resources:
MyLambda:
Type: "AWS::Lambda::Function"
Properties:
Handler: index.handler
Runtime: nodejs12.x
FunctionName: "test-s3-import"
Code:
S3Bucket:
Fn::ImportValue: !Sub ${BucketExportNamePrefix}-BucketOne
S3Key: "index.zip"
Description: "Test Lambda"
MemorySize: 128
Timeout: 60
Role: test-role-arn

Cloudformation YAML custom variable

I am trying to achieve something similar to below in a AWS Cloudformation YAML file:
AWSTemplateFormatVersion: 2010-09-09
testAttribute = "test"
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.7
Role: !GetAtt iam.Arn
MemorySize: 128
Timeout: 10
Handler: lambda_function.lambda_handler
FunctionName: "testName"+${testAttribute}
Description: 'This is my lambda'
Code:
S3Bucket: myBucket
S3Key: "lambda/testName"+${testAttribute}+".zip"
I know that above isn't quite correct, but I cant find a good answer when searching how to achieve it. Anyone who have some guidance on this matter?
It depends on the use case but if the "variable" would be static and you don't need the change it when deploying the stack, I would suggest an alternative solution, to use the Mappings section.
This allows you to define some static values without sending them when deploying the stack (you will have much cleaner deploy commands, and the logic would be on the template side instead of the deploy side).
In this case, I'm using !Sub intrinsic function with a mapping (you can set multiple variables to be substituted using !Sub):
AWSTemplateFormatVersion: 2010-09-09
Mappings:
attributes:
lambda:
testAttribute: "test"
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.7
Role: !GetAtt iam.Arn
MemorySize: 128
Timeout: 10
Handler: lambda_function.lambda_handler
FunctionName: !Sub
- "testName${attr}"
- {attr: !FindInMap [attributes, lambda, testAttribute]}
Description: 'This is my lambda'
Code:
S3Bucket: myBucket
S3Key: !Sub
- "lambda/testName${attr}.zip"
- {attr: !FindInMap [attributes, lambda, testAttribute]}
Note: Mappings have a mandatory three-level nesting, take this into consideration while designing your solution
You could use Parameters with a default value, and Sub later in the template:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
testAttribute:
Type: String
Default: test
Resources:
Lambda:
Type: AWS::Lambda::Function
Properties:
Runtime: python3.7
Role: !GetAtt iam.Arn
MemorySize: 128
Timeout: 10
Handler: lambda_function.lambda_handler
FunctionName: !Sub "testName${testAttribute}"
Description: 'This is my lambda'
Code:
S3Bucket: myBucket
S3Key: !Sub "lambda/testName${testAttribute}.zip"
[Edited for typo]

Passing values from parent stack to nested stack for Cloudformation

I am new to nested stack and i am trying to pass input parameters from parent to child template. My parent stack looks like below:
AWSTemplateFormatVersion: "2010-09-09"
Transform: 'AWS::Serverless-2016-10-31'
Description: "ParentStack with all child stack"
Parameters:
AccountName:
Description: Please Enter valid Account Name.
Type: "CommaDelimitedList"
Default: "citi"
Region:
Description: Enter Region
Type: "CommaDelimitedList"
Default: "us-east-2"
S3BucketName:
Type: "CommaDelimitedList"
Default: ""
S3KeyName:
Type: "CommaDelimitedList"
Default: "Test-LambdaFunction.zip"
Resources:
LambdaStack1:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
TemplateURL: https://test272t3.s3.us-east-2.amazonaws.com/CFTemplates/lambda.yaml
CodeUri:
Bucket: Fn:Join [ ' ', [!Ref S3BucketName] ]
Key: Fn::Join [ ' ', [!Ref S3KeyName] ]
S3Stack:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://test272t3.s3.us-east-2.amazonaws.com/CFTemplates/s3child.yaml
Parameters:
BucketName: <<not sure how !sub can be paased in parent stack>>
AccessControl: PublicReadWrite
VersioningConfiguration:
Status: Suspended
And part of child template is as follows:
Parameters:
AccountName:
Description: Please Enter valid Account Name.
Type: String
Default: citi
Region:
Description: Enter Region
Type: String
Default: us-east-2
S3BucketName:
Type: "String"
Default: ""
S3KeyName:
Type: "String"
Default: "MeghFlow-DBConnMgmt-Lambda-DBConnMgmtFunction.zip"
testLambda:
Type: AWS::Serverless::Function
Properties:
CodeUri:
Bucket: !Ref S3BucketName
Key: !Ref S3KeyName
Handler: com.testff.testinghand.dbconnmgmt.lambda.testLambda::handleRequest
Runtime: java8
MemorySize: 1024
Policies: AmazonDynamoDBFullAccess
Environment:
Variables:
REGION: us-east-2
DYNAMODB_NAME: DBConnectionInfo
ArtifactBucket:
Type: AWS::S3::Bucket
DeletionPolicy: Delete
Properties:
BucketName: !Sub ${AccountName}-${Region}-artifacts
AccessControl: PublicReadWrite
VersioningConfiguration:
Status: Suspended
Issue: I am not quite sure how the input parameters can be passed from parent to child. I referred few links like but i was confused even more as to when the input type must be CommaDelimitedList vs string. I even tried keeping the param type to string in both parent and child but still i get below error:
"Value of property Parameters must be an object with String (or simple type) properties"
and on using Fn::join get error as below:
"Fn::Join object requires two parameters, (1) a string delimiter and (2) a list of strings to be joined or a function that returns a list of strings (such as Fn::GetAZs) to be joined."
Have referred link : Trying to pass parameters from Master to child template but no luck .
Can anyone guide me in correct direction please. Thanks in advance.
Thanks #gandaliter for your guidance. As per above marked answer CloudFormation parent stack accepts only strings and not any object level parameters(sub parameters under parameters just like CodeURI in my above code). I did few tweaks and changed all the parent template to below :
Note: All the parameter type are set to String in child and parent template
AWSTemplateFormatVersion: "2010-09-09"
Transform: 'AWS::Serverless-2016-10-31'
Description: "ParentStack with all child stack"
Parameters:
apiGatewayStageName:
Type: String
Default: "dev"
HandlerName:
Type: String
Default: "com.test.tehgsaLambda::handleRequest"
S3BucketName:
Type: String
Default: ""
S3KeyName:
Type: String
Default: "Test-LambdaFunction.zip"
Resources:
LambdaStack1:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL:
Fn::Sub: "https://testyu2y73.s3.us-east-2.amazonaws.com/CFTemplates/lambda.yaml"
Parameters:
S3BucketName: !Ref S3BucketName
S3KeyName: !Ref S3KeyName
HandlerName: !Ref HandlerName
apiGatewayStageName: !Ref apiGatewayStageName
```
I imagine the code you've given above isn't the only combination you've tried, and the parameters don't exactly line up between your parent and child stack, but in any case, the problem is that you're trying to give parameter values of:
CodeUri:
Bucket: Fn:Join [ ' ', [!Ref S3BucketName] ]
Key: Fn::Join [ ' ', [!Ref S3KeyName] ]
and
VersioningConfiguration:
Status: Suspended
Both of these are objects, not 'String (or simple type) properties'. The error is saying that the whole Parameters object must have only simple values.
Incidentally, TemplateURL needs to go outside the Parameters object.

Value of property Parameters must be an object with String (or simple type) properties

I am trying to pass parameters to one of the nested stacks by populating the values from another nested stacks output.
And i do not want any cross-referencing (unless there is no way around it)
The idea is pretty straight forward.
RootStack
-NstdStackVPC
-NstdStackSG
-NstdStackEC2
The problem is on the last nested stack while creating EC2.
If i created the resource in the root stack directly the EC2 gets created
Description: RootStack
Parameters:
MyKeyName:
Type: AWS::EC2::KeyPair::KeyName
Default: my-test-key
EC2ImageId:
Type: AWS::EC2::Image::Id
Default: ami-0dxxxxa
Resources:
NstdStackVPC ......
NstdStackSG ......
EC2Host:
Type: AWS::EC2::Instance
Properties:
SubnetId: !GetAtt NstdStackVPC.Outputs.VPCPubSubnet
ImageId: !Ref EC2ImageId
InstanceType: t2.micro
KeyName: !Ref MyKeyName
SecurityGroupIds:
- !GetAtt NstdStackSG.Outputs.SecGrp4EC2Host
But if i try to create the EC2 as a nested stack
AWSTemplateFormatVersion: '2010-09-09'
Description: NstdStackEC2.
Parameters:
myNstdKeyName:
Type: AWS::EC2::KeyPair::KeyName
myNstdImageId:
Type: AWS::EC2::Image::Id
myNstdSecGrp:
Type: AWS::EC2::SecurityGroup::Id
myNstdEC2HostSubnet:
Type: AWS::EC2::Subnet::Id
Resources:
EC2Host:
Type: AWS::EC2::Instance
Properties:
SubnetId: !Ref myNstdEC2HostSubnet
ImageId: !Ref myNstdImageId
InstanceType: t2.micro
KeyName: !Ref myNstdKeyName
SecurityGroupIds:
- Ref myNstdSecGrp
By changing the root stack as follows
AWSTemplateFormatVersion: '2010-09-09'
Description: RootStack
Parameters:
MyKeyName:
Type: AWS::EC2::KeyPair::KeyName
Default: my-test-key
EC2ImageId:
Type: AWS::EC2::Image::Id
Default: ami-0dxxxxa
Resources:
NstdStackVPC ......
NstdStackSG ......
NstdStackEC2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://bkt.s3.eu-central-1.amazonaws.com/NstdEC2Host.yml
Parameters:
myNstdKeyName: !Ref MyKeyName
myNstdImageId: !Ref EC2ImageId
myNstdSecGrp: !GetAtt NstdStackSG.Outputs.SecGrp4BasHost
myNstdEC2HostSubnet: !GetAtt NstdStackVPC.Outputs.VPCPubSubnet
It gives me the following error:
Value of property Parameters must be an object with String (or simple type) properties
tried removing all the parameters to try one by one. But it fails on everything.
Even for the parameters that are being referenced directly from the root stack i.e., MyKeyName, EC2ImageId
I ran into the same exact error message with a similar problem and solution. I came here and since the issue was slightly different, this question helped me get to my solution. So, not trying to hijack this question, simply hoping to provide what I found additionally useful to the next person visiting.
I was nesting a cluster template very similar to this one and OPs example. Passing Subnets as a list of strings (I believe List<AWS::Some::Type> will also work).
Subnets:
Description: Subnets of the of the cluster availaibility zone
Type: CommaDelimitedList
Default: subnet-0d..de,subnet-0e..7a,subnet-0b..24
And I'm using the above parameters to call the partial child template as follows.
ECS:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://xx.amazonaws.com/yy/zz.yaml
Parameters:
SecurityGroups: !Join [",", [!GetAtt SecurityGroups.Outputs.ECSHostSecurityGroup]]
Subnets: !Join [",", !Ref Subnets]
So, In the above example, the SecurityGroups are joined together into a list from the output of the SecurityGroup Nested Template, but the subnets are simply joined together from the comma delimited parameter. There is a knowledge-center article too, if you want more info. TA OP
Ok finally sorted this out myself.
In my NstdStackSG outputs section i was referring to the object itself.
And that is where this goes wrong.
AWSTemplateFormatVersion: 2010-09-09
Description: Security group nested stack
Resources:
MySecGrp
Type: ....
.....
....
Outputs:
MyOtptSecGrp:
#This one is working for me.
Value: !GetAtt MySecGrp.GroupId
#previously i was assigning the following value
#Value: !Re MySecGrp
And now in the RootStack
AWSTemplateFormatVersion: '2010-09-09'
Description: RootStack
Parameters:
MyKeyName:
Type: AWS::EC2::KeyPair::KeyName
Default: my-test-key
EC2ImageId:
Type: AWS::EC2::Image::Id
Default: ami-0dxxxxa
Resources:
NstdStackVPC ......
NstdStackSG ......
NstdStackEC2:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: https://bkt.s3.eu-central-1.amazonaws.com/NstdEC2Host.yml
Parameters:
myNstdKeyName: !Ref MyKeyName
myNstdImageId: !Ref EC2ImageId
myNstdSecGrp: !GetAtt NstdStackSG.Outputs.SecGrp4BasHost
myNstdEC2HostSubnet: !GetAtt NstdStackVPC.Outputs.VPCPubSubnet
And in my nestedEC2Stack
AWSTemplateFormatVersion: 2010-09-09
Description: NstdStackEC2
Parameters:
myNstdSecGrp:
Type: AWS::EC2::SecurityGroup::Id
myNstdEC2HostSubnet:
Type: AWS::EC2::Subnet::Id
myNstdKeyName:
Type: AWS::EC2::KeyPair::KeyName
myNstdImageId:
Type: AWS::EC2::Image::Id
Resources:
EC2Host:
Type: AWS::EC2::Instance
Properties:
SubnetId: !Ref myNstdEC2HostSubnet
ImageId: !Ref myNstdImageId
InstanceType: t2.micro
KeyName: !Ref myNstdKeyName
SecurityGroupIds:
- !Ref myNstdSecGrp
Hope this helps. (If not in solving then at least in pointing the right direction)

Use SSM parameter whose name depends on a CFN parameter

I have a CloudFormation template that looks something like the following:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
env:
Type: String
Default: NONE
Resources:
GraphQLAPI:
Type: AWS::AppSync::GraphQLApi
Properties:
Name: !Sub 'my-api-${env}'
AuthenticationType: AMAZON_COGNITO_USER_POOLS
UserPoolConfig:
UserPoolId: <something>
AwsRegion: !Ref AWS::Region
DefaultAction: ALLOW
Suppose that I already have a SSM parameter named /dev/cognitoUserPoolId. When I create this template, passing env=dev, I want to use the value of that parameter as the UserPoolId. I want to avoid manually passing a new CFN parameter for every SSM parameter, as there may be quite a few in practice.
You should be able to use dynamic references to the SSM parameters in your template.
Something like:
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
env:
Type: String
Default: NONE
Resources:
GraphQLAPI:
Type: AWS::AppSync::GraphQLApi
Properties:
Name: !Sub 'my-api-${env}'
AuthenticationType: AMAZON_COGNITO_USER_POOLS
UserPoolConfig:
UserPoolId: !Sub '{{resolve:ssm:/${env}/cognitoUserPoolId:1}}'
AwsRegion: !Ref AWS::Region
DefaultAction: ALLOW
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html