AWS Cloudformation: Cross Stack Reference - amazon-web-services

I have the following stack structure.
ParentStack:
ChildStack1
ChildStack2
What I need to do is to pass LambdaExecutionRole arn created in ChildStack2, having ChildStack2Name as a parameter, to ChildStack1.
Here are the snippets of my script
Parent Stack:
"Resources": {
"ChildStack1": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"ChildStack2Name": {"Fn::GetAtt": ["ChildStack2", "Outputs.StackName"]}
},
....
ChildStack1:
"Parameters" {
"ChildStack2Name": {
"Type": "String",
"Description": "Name of commons stack"
},
...
"Resources": {
"Role": {
HERE I NEED A LambdaExecutionRole ARN CREATED IN CHILDSTACK2
}
}
ChildStack2
"Resources": {
"LambdaExecutionRole": {
.....
}
}
"Outputs": {
"LambdaExecutionRole": {
"Value": {"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]},
"Export": {
"Name": {
"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]
}
}
},
"StackName": {
"Value": {"Ref": "AWS::StackName"},
"Export": {
"Name": {"Ref": "AWS::StackName"}
}
}
}
}
I'm new to cloudformation and probably these snippets could be refactored in some way.
Thank you.

First, you should output the LambdaExecutionRole's ARN from the template. Example below is assuming the resource LambdaExecutionRole in ChildStack2 is an AWS::IAM::Role type and the output name is LambdaExecutionRoleARN.
"Outputs": {
"LambdaExecutionRole": {
"Value": {"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]},
"Export": {
"Name": {
"Fn::Join" : [ ":", [ { "Ref" : "AWS::StackName" }, "LambdaExecutionRole" ]]
}
}
},
"LambdaExecutionRoleARN": {
"Value": {"Fn::GetAtt" : ["LambdaExecutionRole", "Arn"]}
},
Next, in ChildStack1, add another parameter to hold the ARN value that will be passed from Parent Stack. Then you can Ref the parameter into Role resource.
"Parameters" {
"ChildStack2Name": {
"Type": "String",
"Description": "Name of commons stack"
},
"LambdaExecutionRoleArn": {
"Type": "String",
"Description": "Lambda Execution Role's ARN"
},
...
"Resources": {
"Role": {
// Here use { "Ref": "LambdaExecutionRoleArn"} to get the value of the parameters
}
}
Then in your Parent Stack, you can pass the ARN from ChildStack2 to ChildStack 1 using Fn::GetAtt
"Resources": {
"ChildStack1": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"ChildStack2Name": {"Fn::GetAtt": ["ChildStack2", "Outputs.StackName"]}
"LambdaExecutionRoleArn": {"Fn::GetAtt": ["ChildStack2", "Outputs.LambdaExecutionRoleARN"]}
},
....

Related

Aws cloudformation - how to use a string parameter to prevent repetitive use of same string

1.
In my code, string "HelloWorldApi" uses a lots as a ref.
Is it possbile to use my defined parameter "APIName" to replace the repeat string "HelloWorldApi", so that I don't need to update those string 1 by 1 when I make another stack and rename the API?
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"APIName": {
"Type": "String",
"Default": "HelloWorldApi"
}
},
"Resources": {
"HelloWorldApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "hello-api",
"Description": "API used for practice",
"FailOnWarnings": true
}
},
"APIAuthorizer" :{
"Type" : "AWS::ApiGateway::Authorizer",
"Properties" : {
"RestApiId" : {
"Ref": "HelloWorldApi"
}
}
},
"BannerDBModel": {
"Type" : "AWS::ApiGateway::Model",
"Properties" : {
"Name" : "postBannerModel",
"RestApiId" : {
"Ref": "HelloWorldApi"
},
"Schema" : {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ProductsInputModel",
"type": "object",
"properties": {
"url": {"type": "string"}
}
}
}
},
"PostRequestValidator": {
"Type" : "AWS::ApiGateway::RequestValidator",
"Properties" : {
"Name" : "PostRequestValidator",
"RestApiId" : {
"Ref": "HelloWorldApi"
}
}
},
"BannerResource": {
"Type": "AWS::ApiGateway::Resource",
"Properties": {
"RestApiId": {
"Ref": "HelloWorldApi"
},
"ParentId": {
"Fn::GetAtt": [
"HelloWorldApi",
"RootResourceId"
]
},
"PathPart": "banner"
}
},
"getBannerMethod": {
"Type": "AWS::ApiGateway::Method",
"DependsOn": ["HelloWorldApi"],
"Properties": {
"RestApiId": {
"Ref": "HelloWorldApi"
},
"ResourceId": {
"Ref": "BannerResource"
},
"HttpMethod": "GET",
"AuthorizationType": "NONE"
}
},
"Deployment": {
"DependsOn": ["HelloWorldApi"],
"Type": "AWS::ApiGateway::Deployment",
"Properties": {
"RestApiId": {
"Ref": "HelloWorldApi"
}
}
}
}
}
2.
Lets say I have already created a stack through below code, if the default value of APIName is accidentally set to HelloWorld which is different from original version, will the error shows instantly after I run updateStack? What will happen then? Another API HelloWorld being created?
Updated Code according to the comment
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"RestAPI": {
"Type": "String",
"Default": "HelloWorldApi"
}
},
"Resources": {
"RestAPI": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": "hello-api",
"Description": "API used for practice",
"FailOnWarnings": true
}
},
"APIAuthorizer" :{
"Type" : "AWS::ApiGateway::Authorizer",
"Properties" : {
"RestApiId" : {
"Ref": "RestAPI"
}
}
},
"BannerDBModel": {
"Type" : "AWS::ApiGateway::Model",
"Properties" : {
"Name" : "postBannerModel",
"RestApiId" : {
"Ref": "RestAPI"
},
"Schema" : {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ProductsInputModel",
"type": "object",
"properties": {
"url": {"type": "string"}
}
}
}
}
}
}
To reuse this template you should rename the resource named HelloWorldApi to something more generic.
If you currently renamed the resource and attempted to redeploy it would remove any resources that are associated with the HelloWorldApi API as well as the API itself whilst redeploying the API again.
The string you're using is referencing the AWS::ApiGateway::RestApi resource not the value in the APIName parameter. If you updated this parameter value at the moment it would not affect the stack as it appears as though it is not used.
In summary the referencing of the string HelloWorldApi in Resources is referring to the AWS::ApiGateway::RestApi resources logical name.
Ensure the resources do not share the same name as the parameters.
The template would look like the below
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"RestAPIName": {
"Type": "String",
"Default": "HelloWorldApi"
}
},
"Resources": {
"RestAPI": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Name": { "Ref": "RestAPIName" },
"Description": "API used for practice",
"FailOnWarnings": true
}
},
"APIAuthorizer" :{
"Type" : "AWS::ApiGateway::Authorizer",
"Properties" : {
"RestApiId" : {
"Ref": "RestAPI"
}
}
},
"BannerDBModel": {
"Type" : "AWS::ApiGateway::Model",
"Properties" : {
"Name" : "postBannerModel",
"RestApiId" : {
"Ref": "RestAPI"
},
"Schema" : {
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "ProductsInputModel",
"type": "object",
"properties": {
"url": {"type": "string"}
}
}
}
}
}
}

Cloudformation Property validation failure: Encountered unsupported properties

I'm trying to create a nested stack with the root stack looks like this:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"DynamoDBTable": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"TableName": {
"Fn::Sub": "${AWS::StackName}"
}
},
"TemplateURL": "https://s3.amazonaws.com/my-templates-bucket/dynamodb.json"
}
},
"S3WebsiteReact": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"BucketName": {
"Fn::Sub": "${AWS::StackName}-website"
}
},
"TemplateURL": "https://s3.amazonaws.com/my-templates-bucket/s3-static-website-react.json"
}
},
"S3UploadBucket": {
"Type": "AWS::CloudFormation::Stack",
"Properties": {
"Parameters": {
"BucketName": {
"Fn::Sub": "${AWS::StackName}-upload"
}
},
"TemplateURL": "https://s3.amazonaws.com/my-templates-bucket/s3-with-cors.json"
}
},
"Cognito": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": "DynamoDBTable",
"Properties": {
"Parameters": {
"CognitoUserPoolName": {
"Fn::Join" : ["",
{
"Fn::Split": ["-", {
"Ref": "AWS::StackName"
}]
}
]
}
},
"TemplateURL": "https://s3.amazonaws.com/my-templates-bucket/cognito.json"
}
},
"ApiGateway": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": ["DynamoDBTable", "Cognito"],
"Properties": {
"Parameters": {
"ApiGatewayName": {
"Fn::Sub": "${AWS::StackName}-api"
},
"CognitoUserPoolArn": {
"Fn::GetAtt": [ "Cognito", "Outputs.UserPoolArn" ]
},
"DynamoDBStack": {
"Fn::GetAtt": [ "DynamoDBTable", "Outputs.DDBStackName" ]
}
},
"TemplateURL": "https://s3.amazonaws.com/my-templates-bucket/api-gateway.json"
}
},
"IdentityPool": {
"Description": "Cognito Identity Pool. Must be created after User Pool and API Gateway.",
"Type": "AWS::Cognito::IdentityPool",
"DependsOn": ["Cognito", "ApiGateway", "S3UploadBucket"],
"Properties": {
"Parameters": {
"AppClientId": {
"Fn::GetAtt": [ "Cognito", "Outputs.AppClientId" ]
},
"UserPoolProviderName": {
"Fn::GetAtt": [ "Cognito", "Outputs.ProviderName" ]
},
"UserPoolName": {
"Fn::GetAtt": [ "Cognito", "Outputs.UserPoolName" ]
},
"UploadBucketName": {
"Fn::GetAtt": [ "S3UploadBucket", "Outputs.UploadBucketName" ]
},
"ApiGatewayId": {
"Fn::GetAtt": [ "ApiGateway", "Outputs.ApiGatewayId" ]
}
},
"TemplateURL": "https://s3.amazonaws.com/my-templates-bucket/identity-pool.json"
}
}
},
"Outputs": {
}
}
And I get this error:
2019-06-19 14:45:14 UTC-0400 IdentityPool CREATE_FAILED Property validation failure: [Encountered unsupported properties in {/}: [TemplateURL, Parameters]]
It looks like my identity pool stack has some issues with the parameters. But the identity pool stack parameters look like this:
"Parameters" : {
"AppClientId": {
"Description": "ID of the App Client of the Cognito User Pool passed into this stack.",
"Type": "String"
},
"UserPoolProviderName": {
"Description": "Cognito User Pool Provider name passed into this stack.",
"Type": "String"
},
"UserPoolName": {
"Description": "Cognito User Pool Name passed into this stack.",
"Type": "String"
},
"UploadBucketName": {
"Description": "Name of the bucket that is used to upload files to.",
"Type": "String"
},
"ApiGatewayId": {
"Description": "ID of the API Gateway created for the stack.",
"Type": "String"
}
},
The funny thing is: I tried creating each stack on its own, then passed the outputs from them as parameters to the stacks that need those parameters and every single stack was created successfully without any problems.
I've tried to look for what is unsupported but was unable to find any answers.
The error:
[Encountered unsupported properties in {/}: [TemplateURL, Parameters]]
Says that those two properties are unsupported. Unlike all the rest of the resources declared in your template which also use those two properties, this resource is a AWS::Cognito::IdentityPool, while the rest are all of type AWS::CloudFormation::Stack.
Those two properties are only valid on the AWS::CloudFormation::Stack type, hence the validation error.

AWS CloudFormation template "Launch configuration name not found"

I came across this issue with AWS CloudFormation template I'm creating. I am creating an AutoScaling group and assign LaunchConfiguration to it, but when I run the template I get the error "Launch configuration name not found - A launch configuration with the name: WebServerASLaunchConfig does not exists". Here is the exact code snippet
"WebServerASLaunchConfig": {
"Type" : "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Ref": "BaseImageId"
},
"KeyName": {
"Ref": "KeyPairName"
},
"AssociatePublicIpAddress" : "True",
"InstanceType": "t2.small",
"SecurityGroups": [
{
"Ref": "EC2InstanceSecurityGroup"
}
]
}
},
"WebServerAutoScalingGroup": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"LaunchConfigurationName": "WebServerASLaunchConfig",
"AvailabilityZones": [
{
"Ref": "AvailabilityZone1"
},
{
"Ref": "AvailabilityZone2"
}
],
"VPCZoneIdentifier": [
{
"Ref" : "PublicSubnet1"
},
{
"Ref" : "PublicSubnet2"
}
],
"MinSize" : "2",
"MaxSize" : "2",
"LoadBalancerNames": [
{
"Ref" : "ApplicationLoadBalancer"
}
],
"Tags": [
{
"Key": "Name",
"Value": {
"Fn::Join": [
"-",
[
{
"Ref": "AWS::StackName"
},
"VPC"
]
]
},
"PropagateAtLaunch": "True"
}
]
}
}
Thanks for the help
To reference any parameters or resources make use of Ref.
Replace "LaunchConfigurationName": "WebServerASLaunchConfig",
with:
"LaunchConfigurationName": { "Ref": "WebServerASLaunchConfig" }

Can I make template for Resource in Cloudformation

I tried using the following as template for my resource, in this example, the autoscaling group.
"Outputs": {
"TemplateGroupSettings": {
"Properties": {
"AvailabilityZones" : { "Fn::GetAZs" : "" },
"LaunchConfigurationName": { "Ref": "LaunchConfig" },
"MaxSize": "3",
"MinSize": "1",
"Tags": [
{ "Key": "swarm:master", "Value": {"Ref": "JenkinsURL"}, "PropagateAtLaunch": "true" },
{ "Key": "swarm:username", "Value": {"Ref": "JenkinsUsername"}, "PropagateAtLaunch": "true" },
{ "Key": "swarm:executors", "Value": {"Ref": "JenkinsExecutors"}, "PropagateAtLaunch": "true" },
{ "Key": "ci:username", "Value": {"Ref": "CIUsername"}, "PropagateAtLaunch": "true" }]
},
"Type": "AWS::AutoScaling::AutoScalingGroup",
"UpdatePolicy": {
"AutoScalingRollingUpdate": {
"MaxBatchSize": "1",
"MinInstancesInService": "1",
"WaitOnResourceSignals": "true",
"PauseTime": "PT1M5S"
}
}
}
},
But this option doesn't work
"Resources": {
....
"TemplateGroup": { "Ref": "TemplateGroupSettings" },
Is there any other way around ? Because this one doesn't work
$ euform-create-stack --region eucalyptus -p InstanceType=t1.micro -p EnvironmentType=production --template-file ~cftemplate.json --capabilities CAPABILITY_IAM teststack
euform-create-stack: error (ValidationError): Type is a required property of Resource

AWS::AutoScaling::LaunchConfiguration You are not authorized to perform this operation

While cloudformation is building the stack, I get the following error:
AWS::AutoScaling::LaunchConfiguration N1ClusterServerLaunchConfig You are not authorized to perform this operation
I have admin full access user privileges. But, still this fails. Is the authorization due to the IAMs defined inside the template?
"N1ClusterServerAutoScale": {
"Type": "AWS::AutoScaling::AutoScalingGroup",
"Properties": {
"AvailabilityZones": {
"Fn::GetAZs": ""
},
"LaunchConfigurationName": {
"Ref": "N1ClusterServerLaunchConfig"
},
"MinSize": "2",
"MaxSize": "64",
"DesiredCapacity": {
"Ref": "ClusterSize"
},
"Tags": [
{
"Key": "Name",
"Value": {
"Ref": "AWS::StackName"
},
"PropagateAtLaunch": true
}
]
},
"Metadata": {
"AWS::CloudFormation::Designer": {
"id": "a715af00-ebba-4fab-a817-d5ee1986dfe7"
}
}
},
"N1ClusterServerLaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"RegionMap",
{
"Ref": "AWS::Region"
},
"hvm"
]
},
"InstanceType": {
"Ref": "InstanceType"
},
"KeyName": {
"Ref": "KeyPair"
},
"SecurityGroups": [
{
"Ref": "N1ClusterSecurityGroup"
},
{
"Ref": "N1NodeSecurityGroup"
}
],
"IamInstanceProfile": {
"Ref": "IamInstanceProfile"
},
IAM
"AllowComputeFrom": {
"Description": "The net block (CIDR) that N1-COMPUTE is available to.",
"Default": "0.0.0.0/0",
"Type": "String"
},
"IamInstanceProfile": {
"Description": "The name of an IAM Profile which can access required S3 buckets and instances.",
"Default": "arn:aws:iam::247256189695:instance-profile/n1-compute-instance",
"Type": "String"
},
"IamInstanceProfileShort": {
"Description": "The last part of the name of an IAM Profile which can create instances.",
"Default": "n1-compute-instance",
"Type": "String"
},
ec2:RunInstances is required to use a Launch template