Outputs are not getting generated based on condition - amazon-web-services

In my cf template I have set of conditions defined and those conditions are invoked in the resource section as well however when i try to generate outputs using the conditions its not working as expected.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "CloudFormation to Deploy EMR clusters",
"Parameters": {
"Applications": {
"Default": "Core Hadoop",
"Description": "Installed Applications",
"Type": "String",
"AllowedValues" : ["Core Hadoop","HBase"],
"ConstraintDescription": "Must be valid Applications"
},
"awsRegion": {
"Default" : "<my-region>",
"Description": "awsRegion",
"Type": "String"
},
"Ec2KeyName": {
"Default": "<my-key.pem>",
"Description": "Ec2KeyName",
"Type": "String"
}
},
"Resources": {
"EMRCluster1": {
"Type" : "AWS::EMR::Cluster",
"Condition" : "CH",
"Properties" : {
"Applications" : [
{
"Name": "Hadoop"
},
{
"Name": "Hive"
},
{
"Name": "Hue"
},
{
"Name": "Pig"
},
{
"Name": "Mahout"
},
{
"Name": "Tez"
}
]
}
}
"EMRCluster2": {
"Type" : "AWS::EMR::Cluster",
"Condition" : "HB",
"Properties" : {
"Applications": [
{
"Name": "Hadoop"
},
{
"Name": "Hive"
},
{
"Name": "Hue"
},
{
"Name": "HBase"
},
{
"Name": "Phoenix"
},
{
"Name": "ZooKeeper"
}
]
}
}
}
"Conditions" : {
"CH" : {"Fn::Equals" : [{"Ref" : "Applications"}, "Core Hadoop"]},
"HB" : {"Fn::Equals" : [{"Ref" : "Applications"}, "HBase"]}
},
"Outputs": {
"MasterPublicDnsName": {
"Condition" : "CH",
"Value": {
"Fn::GetAtt": [
"EMRCluster1",
"MasterPublicDNS"
]
},
"Description": "MasterPublicDNS for cluster"
},
"MasterPublicDnsName": {
"Condition" : "HB",
"Value": {
"Fn::GetAtt": [
"EMRCluster2",
"MasterPublicDNS"
]
},
"Description": "MasterPublicDNS for cluster"
}
}
Expected: If i choose "CH" in the parameters then it should give cluster1 masterdns and if i choose "HB" it should provide cluster2's masterdns
Actual: If i choose "HB" which is the last section in output, it will give me the masterdns of cluster2 however if i choose "CH" outputs section in cloudformation gives me "No outputs found.".
Can somebody help me on this please.

The problem probably exists because you are using the same name for both outputs. Since the behaviour of JSON with duplicate keys is undefined, the implementing language can choose how to behave in this situation. Presumably, the second time you are using the MasterPublicDnsName as the output name, you are overwriting the first name, which is consistent with the behaviour you are seeing.
You can either opt to use two different names, but this might makes using cross-stack references difficult, or use an Fn::If statement in the value of the output:
"Fn::If": [condition_name, value_if_true, value_if_false]
Or in your case:
"Fn::If": ["CH", {"Fn::GetAtt": ["EMRCluster1", "MasterPublicDNS"]}, {"Fn::GetAtt": ["EMRCluster2", "MasterPublicDNS"]}]
If you need more than two options, you'll need to nest your Fn::If statements

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"}
}
}
}
}
}
}

How to create AWS Elasticbeanstalk application with integrated RDS using CloudFormation?

I am trying to setup the AWS environment for my SpringBoot application which uses Postgres.
I decided to use CloudFormation to configure all the AWS components needed for my application.
Following is the cloud formation template app.json:
{
"AWSTemplateFormatVersion": "2010-09-09",
"Parameters": {
"S3BucketName": {
"Description": "S3 BucketName",
"Type": "String"
},
"S3FileName": {
"Description": "Name of the jar/war file",
"Type": "String"
},
"DBName":{
"Default":"mytododb",
"Description":"The database name",
"Type":"String",
"MinLength":"1",
"MaxLength":"64",
"AllowedPattern":"[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription":"must begin with a letter and contain only alphanumeric characters."
},
"DBUser":{
"Description":"The database admin account username",
"Type":"String",
"MinLength":"1",
"MaxLength":"16",
"AllowedPattern":"[a-zA-Z][a-zA-Z0-9]*",
"ConstraintDescription":"must begin with a letter and contain only alphanumeric characters."
},
"DBPassword":{
"NoEcho":"true",
"Description":"The database admin account password",
"Type":"String",
"MinLength":"8",
"MaxLength":"41",
"AllowedPattern":"[a-zA-Z0-9]*",
"ConstraintDescription":"must contain only alphanumeric characters."
},
"DBAllocatedStorage":{
"Default":"5",
"Description":"The size of the database (Gb)",
"Type":"Number",
"MinValue":"5",
"MaxValue":"1024",
"ConstraintDescription":"must be between 5 and 1024Gb."
},
"DBInstanceClass":{
"Default":"db.t2.micro",
"Description":"The database instance type",
"Type":"String",
"ConstraintDescription":"must select a valid database instance type."
},
"MultiAZDatabase":{
"Default":"false",
"Description":"Create a multi-AZ RDS database instance",
"Type":"String",
"AllowedValues":[
"true",
"false"
],
"ConstraintDescription":"must be either true or false."
}
},
"Resources": {
"SpringBootApplication": {
"Type": "AWS::ElasticBeanstalk::Application",
"Properties": {
"Description":"Spring boot and elastic beanstalk"
}
},
"SpringBootApplicationVersion": {
"Type": "AWS::ElasticBeanstalk::ApplicationVersion",
"Properties": {
"ApplicationName":{"Ref":"SpringBootApplication"},
"SourceBundle": {
"S3Bucket": {
"Ref": "S3BucketName"
},
"S3Key": {
"Ref": "S3FileName"
}
}
}
},
"SpringBootBeanStalkConfigurationTemplate": {
"Type": "AWS::ElasticBeanstalk::ConfigurationTemplate",
"Properties": {
"ApplicationName": {"Ref":"SpringBootApplication"},
"Description":"A display of speed boot application",
"OptionSettings": [
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "DBAllocatedStorage",
"Value": {
"Ref": "DBAllocatedStorage"
}
},
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "DBDeletionPolicy",
"Value": "Delete"
},
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "DBEngine",
"Value": "postgres"
},
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "DBEngineVersion",
"Value": "10.4"
},
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "DBInstanceClass",
"Value": { "Ref": "DBInstanceClass" }
},
{
"OptionName": "DBPassword",
"Namespace": "aws:rds:dbinstance",
"Value": { "Ref": "DBPassword" }
},
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "DBUser",
"Value": { "Ref": "DBUser" }
},
{
"Namespace": "aws:rds:dbinstance",
"OptionName": "MultiAZDatabase",
"Value": { "Ref": "MultiAZDatabase" }
}
],
"SolutionStackName": "64bit Amazon Linux 2018.03 v2.9.2 running Java 8"
}
},
"SpringBootBeanstalkEnvironment": {
"Type": "AWS::ElasticBeanstalk::Environment",
"Properties": {
"ApplicationName": {"Ref":"SpringBootApplication"},
"EnvironmentName":"TodoAppEnvironment",
"TemplateName": {"Ref":"SpringBootBeanStalkConfigurationTemplate"},
"VersionLabel": {"Ref": "SpringBootApplicationVersion"}
}
}
},
"Outputs": {
"DevURL": {
"Description": "The URL of the DEV Elastic Beanstalk environment",
"Value": {
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"SpringBootBeanstalkEnvironment",
"EndpointURL"
]
}
]
]
},
"Export": {
"Name": {
"Fn::Sub": "${AWS::StackName}-EndpointURL"
}
}
}
}
}
And the parameters file parameters.json is:
[
{
"ParameterKey": "DBUser",
"ParameterValue": "myuser"
},
{
"ParameterKey": "DBPassword",
"ParameterValue": "secret"
},
{
"ParameterKey": "S3BucketName",
"ParameterValue": "todoapp"
},
{
"ParameterKey": "S3FileName",
"ParameterValue": "todo-api-spring-boot-0.0.1.jar"
}
]
I am creating the CloudFormation stack using
aws cloudformation create-stack --template-body file://app.json --parameters file://parameters.json --stack-name=todo-stack
Problem:
The stack is getting created, however the database (RDS) instance is not getting created and when I see in ElasticBeanstalk configuration for the app in Management Console there is no Database configuration associated with the application.
My expectation/assumption is by configuring various aws:rds:dbinstance properties in OptionSettings an RDS instance will be provisioned and associates with ElasticBeanstalk application.
Is my understanding wrong or am I missing any other setting?
PS:
The idea is to use integrated RDS so that I can use RDS_HOST, RDS_PORT etc properties in my application to connect to database.
I am able to configure a separate RDS Resource and connect to it by specifying the connection parameters. But I am expecting aws:rds:dbinstance properties in OptionSettings would create the RDS and associate it to Elasticbeanstalk application. If that is not the case then what is the purpose of configuring those parameters in OptionSettings?
Ref:
https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/command-options-general.html#command-options-general-rdsdbinstance
https://github.com/metabase/metabase/blob/master/bin/aws-eb-docker/cloudformation-elasticbeanstalk.json.template
RDS creation is not done due to the Namespace Approach. Basically Namespace is used to override the default properties of the resources which you have created.
So the solution is to include the RDS creation template before your "SpringBootBeanStalkConfigurationTemplate"
"Resources": {
"RDS Creation": {
{
"Type" : "AWS::RDS::DBInstance",
"Properties" : {
"AllocatedStorage" : String,
"AllowMajorVersionUpgrade" : Boolean,
"AssociatedRoles" : [ DBInstanceRole, ... ],
"AutoMinorVersionUpgrade" : Boolean,
"AvailabilityZone" : String,
"BackupRetentionPeriod" : Integer,
"CharacterSetName" : String,
"CopyTagsToSnapshot" : Boolean,
"DBClusterIdentifier" : String,
"DBInstanceClass" : String,
"DBInstanceIdentifier" : String,
"DBName" : String,
"DBParameterGroupName" : String,
"DBSecurityGroups" : [ String, ... ],
"DBSnapshotIdentifier" : String,
"DBSubnetGroupName" : String,
"DeleteAutomatedBackups" : Boolean,
"DeletionProtection" : Boolean,
"Domain" : String,
"DomainIAMRoleName" : String,
"EnableCloudwatchLogsExports" : [ String, ... ],
"EnableIAMDatabaseAuthentication" : Boolean,
"EnablePerformanceInsights" : Boolean,
"Engine" : String,
"EngineVersion" : String,
"Iops" : Integer,
"KmsKeyId" : String,
"LicenseModel" : String,
"MasterUsername" : String,
"MasterUserPassword" : String,
"MonitoringInterval" : Integer,
"MonitoringRoleArn" : String,
"MultiAZ" : Boolean,
"OptionGroupName" : String,
"PerformanceInsightsKMSKeyId" : String,
"PerformanceInsightsRetentionPeriod" : Integer,
"Port" : String,
"PreferredBackupWindow" : String,
"PreferredMaintenanceWindow" : String,
"ProcessorFeatures" : [ ProcessorFeature, ... ],
"PromotionTier" : Integer,
"PubliclyAccessible" : Boolean,
"SourceDBInstanceIdentifier" : String,
"SourceRegion" : String,
"StorageEncrypted" : Boolean,
"StorageType" : String,
"Tags" : [ Tag, ... ],
"Timezone" : String,
"UseDefaultProcessorFeatures" : Boolean,
"VPCSecurityGroups" : [ String, ... ]
}
}
}
}
After this, you can include the RDS parameters in your other Resources as it will be created first.
I was stumbling across the same question and found some sort of an answer here: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/using-features.managing.db.html
In that document they say:
You can configure your environment's database instance using configuration files. Use the options in the aws:rds:dbinstance namespace. The following example modifies the allocated database storage size to 100 GB.
So the aws:rds:dbinstance properties in OptionSettings are only meant to modify an existing database instance, not to create one in the first place.

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.

How we can create Geolocation through AWS:cloudfromation template?

I have implemented this but it says invalid request. It may totally wrong.
I want to create geolocation for particular country
"Geolocation": {
"Type": "AWS::Route53::RecordSet",
"Properties": {
"HostedZoneName": {
"Ref": "Route53Domain"
},
"Name": {
"Fn::Join": [
"$",
[
{
"Ref": "javadns"
},
{
"Ref": "Route53Domain"
}
]
]
},
"Type": "CNAME",
"TTL": "60",
"GeoLocation": {"CountryCode" : "*"},
"ResourceRecords": [
{ "Fn::Join" : [".", ["geoip", { "Ref" : "javadns" }]]}
]
}
},
To create a Geo Location record you must specify the SetIdentifier property in CloudFormation.
i see you are missing that property in your CloudFormation and probably that is why it is throwing you an error.
Also here are the code countries identifiers that are supported by Route53, you must not specify the wildcard in that parameter, countries and continent codes consist of two letters.
Documentation here.

Can't send parameters from a child CloudFormation template to another child template

I am crafting a monstrosity of a CloudFormation template that uses the AWS::CloudFormation::Stack resource to spawn other nested stacks. The purpose of this is the classic separation of duties and management.
The overall goal is to build an application environment from the ground up (starting with VPC and ending with the application instances).
So far, most of it works. But I've hit a road block with referencing the outputs of the subnet CF template and the securitygroup CF template for use in creating the EC2 instances.
It works like this:
Master template --> Builds VPC --> Calls child template to build subnets --> Calls child template to build security groups --> Calls child template to build EC2 instances
I need to pass the outputs from the subnet and security group templates to the EC2 instance template so the instances can be provisioned into the correct part of the architecture. The VPC ID ref and KeyPairs pass in fine, but the subnetID and securitygroupID do not.
Here's the portion of the master template that calls the security group/subnet templates:
"DevNetworkStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": [
"SNVPC",
"SNIGW"
],
"Properties": {
"TemplateURL": {
"Fn::FindInMap": [
"TemplateURLs",
"DevNetworkStack",
"URL"
]
},
"TimeoutInMinutes": "30",
"Parameters": {
"VPCID": {
"Ref": "SNVPC"
},
"SNIGW": {
"Ref": "SNIGW"
}
}
}
},
"DevSecurityGroupsStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": "DevNetworkStack",
"Properties": {
"TemplateURL": {
"Fn::FindInMap": [
"TemplateURLs",
"DevSecurityGroupsStack",
"URL"
]
},
"TimeoutInMinutes": "30",
"Parameters": {
"VPCID": {
"Ref": "SNVPC"
}
}
}
},
These work fine. They create and everything is fine. The templates offer outputs like so:
"Outputs": {
"DevAdminSubnetID": {
"Description": "DevAdminSubnetID",
"Value": {
"Ref": "DevAdminSubnet"
}
},
...
"Outputs": {
"DevAdminSecurityGroupID": {
"Description": "DevAdminSecurityGroupID",
"Value": {
"Ref": "DevAdminSecurityGroup"
}
},
I can see the outputs in the CF console.
Now, the next template is trying to use the Security Group ID and the Subnet ID. But it's not working.
Master template calls the next child as:
"DevAdminStack": {
"Type": "AWS::CloudFormation::Stack",
"DependsOn": [
"DevNetworkStack",
"DevSecurityGroupsStack",
"EC2DevRoleInstanceProfile",
"S3DevUserDataBucket",
"S3DevHomeDirsDataBucket"
],
"Properties": {
"TemplateURL": {
"Fn::FindInMap": [
"TemplateURLs",
"DevAdminStack",
"URL"
]
},
"TimeoutInMinutes": "30",
"Parameters": {
"AdminKeyPair": {
"Ref": "AdminServersKeyPair"
},
"VPCID": {
"Ref": "SNVPC"
},
"DevAdminSecurityGroupID": [
{
"Fn::GetAtt": [
"DevSecurityGroupsStack",
"Outputs.DevAdminSecurityGroupID"
]
}
],
"DevAdminSubnetID": [
{
"Fn::GetAtt": [
"DevNetworkStack",
"Outputs.DevAdminSubnetID"
]
}
]
}
}
}
...and the child template looks like this (removed some portions for brevity because I'm just testing right now)
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Dev-Admin",
"Mappings": {
"RegionMap": {
"DevAdminServer": {
"AMI": "ami-6fc9770e",
"InstanceType": "t2.micro"
}
}
},
"Parameters": {
"AdminKeyPair": {
"Type": "AWS::EC2::KeyPair::KeyName"
},
"VPCID": {
"Type": "AWS::EC2::VPC::Id"
},
"DevAdminSecurityGroupID": {
"Type": "AWS::EC2::SecurityGroup::Id"
},
"DevAdminSubnetID": {
"Type": "AWS::EC2::Subnet::Id"
}
},
"Resources": {
"DevAdminServer": {
"Type": "AWS::EC2::Instance",
"Metadata": {
"Comment": "Sets up administrative tools for the server",
"AWS::CloudFormation::Init": {
"config": {
"packages": {
"yum": {}
},
"files": {},
"services": {}
}
}
},
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"RegionMap",
"DevAdminServer",
"AMI"
]
},
"SecurityGroupIds": [
{
"Ref": "DevAdminSecurityGroupID"
}
],
"SubnetId": {
"Ref": "DevAdminSubnetID"
},
"InstanceType": {
"Fn::FindInMap": [
"RegionMap",
"DevAdminServer",
"InstanceType"
]
},
"KeyName": {
"Ref": "AdminKeyPair"
},
"Tags": [
{
"Key": "Application",
"Value": {
"Ref": "AWS::StackId"
}
}
],
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[]
]
}
}
}
}
}
}
But this resource fails on creation with the error:
Value of property Parameters must be an object with String (or simple type) properties
I know it's the last two variables causing the trouble (subnetID and securitygroupID), because I removed them and the provisioning the child template works just fine.
What am I doing wrong?
The values of DevAdminSecurityGroupID and DevAdminSubnetID are defined as JSON arrays [] in your master template, but they should be Strings (after the Fn::GetAtt intrinsic function expansion):
"DevAdminSecurityGroupID":
{
"Fn::GetAtt": [
"DevSecurityGroupsStack",
"Outputs.DevAdminSecurityGroupID"
]
},
"DevAdminSubnetID":
{
"Fn::GetAtt": [
"DevNetworkStack",
"Outputs.DevAdminSubnetID"
]
}