Output a list in cloud formation - aws-cloudformation

I have a parameter:
ClusterSubnets:
Description: Subnets where cluster will reside.
Typically private. Use mutiples, each in a different AZ for HA.
ConstraintDescription: comma separated list of valid Subnet IDs
Type: List<AWS::EC2::Subnet::Id>
I'm trying to output this:
ClusterSubnets:
Description: Subnets used by cluster
Value: !Ref ClusterSubnets
Export:
Name: !Sub "${AWS::StackName}-ClusterSubnets"
But I get this error: Template format error: The Value field of every Outputs member must evaluate to a String.
How can I export a list?

You need to join the elements of the list into a string. Try something like this:
ClusterSubnets:
Description: Subnets used by cluster
Value: !Join
- ','
- !Ref ClusterSubnets
Export:
Name: !Sub "${AWS::StackName}-ClusterSubnets"
Here is the relevant AWS documentation.

Related

is it possible to reuse existing route table in cloudformation template

I am writing one cft to connect dynamodb uisng vpcEndpoint.
DynamoDBEndpoint:
Type: "AWS::EC2::VPCEndpoint"
Properties:
RouteTableIds:
- !Ref PublicRouteTable
- !Ref Private0RouteTable
- !Ref Private1RouteTable
- !Ref Private2RouteTable
ServiceName:
!Sub "com.amazonaws.${AWS::Region}.dynamodb"
VpcId: !Ref VPC
Parameters:
vpcId:
Description: Choose the existing one
Type: AWS::EC2::VPC::Id
I am getting existing vpcId by using parameter ,is there any way I can ruse my existing routetable ,Please suggest me on this how could I define this ref PublicRouteTable and ref PrivateRouteTable .
Your parameter vpcId works because it uses AWS::EC2::VPC::Id type which belongs to Supported AWS-specific parameter types in CloudFormation.
Sadly, CloudFormation does not support similar type for route tables. You have to type out route tables IDs manually in console when you specify parameters.

store the values to a variable after executing the template

This cloudformation template is working as expected:
https://github.com/shantanuo/cloudformation/blob/master/updated/esbck.yml
But how do I output the ARN of IAM role that it creates?
To add to Marcins answer, if you export the output, it becomes available for use in other Cloudformation templates deployed in the same AWS account (in the same region)
Add an export to the output:
Outputs:
RoleArn:
Value: !GetAtt EsSnapshotRole.Arn
Export:
Name: EsSnapshotRoleArn
Once this is done, you can use the Fn::ImportValue intrinsic function in other templates
# some-other-template.yml
Resources:
SomeResourceRequiringRoleArn:
Type: AWS::SomeService::SomeResource
Properties:
IamRoleArn: !ImportValue EsSnapshotRoleArn
Have to add output section:
Outputs:
RoleArn:
Value: !GetAtt EsSnapshotRole.Arn

CloudFormation: Return ARN of Subnet

Is there another way to get ARN of created Subnet Resource AWS::EC2::Subnet via Fn::GetAtt intrinsic function. Subnet resource only returns AvailabilityZone, Ipv6CidrBlocks, NetworkAclAssociationId, VpcId.
Documentation: https://docs.aws.amazon.com/en_pv/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-subnet.html#aws-resource-ec2-subnet-return-values
Since the ARN of a Subnet is in the format arn:aws:ec2:REGION:ACCOUNT_ID:subnet/SUBNET_ID. By using intrinsic function Fn::Join you can generate the ARN of the subnet.
Example: arn:aws:ec2:ap-southeast-1:767022272945:subnet/subnet-0d42d2235s3a2531d
!Join
- ''
- - 'arn:aws:ec2:'
- !Ref 'AWS::Region'
- ':'
- !Ref 'AWS::AccountId'
- ':subnet/'
- Fn::ImportValue:
Fn::Sub: VPC-SubnetId
A simpler solution, as noted in the comment by #sigpwned, is to just use !Sub
Even if the subnet you're referencing isn't local to your template, you can still pass it in as a parameter to the template or import it like in the original answer if it is available to the stack.
!Sub "arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:subnet/${Subnet}"

How to work with variables in Cloudformation template as the feature of "Data source" in terraform

In terraform, we can use data source to get the exist resource details.
But in cloudformation, if the resource isn't created by cloudformation template, I can't find any ways to reference it, unless I hard code the value, such as real vpc id.
Any suggestions?
One way you could do this is use Parameter (e.g. AWS::EC2::VPC::Id for vpc ID). While creating a stack, this will list out all the existing vpcs in the region you're creating the stack in. Currently such parameters are limited to few such as volume, subnet, sg but more may come in future. Though currently there's not any exact equivalent of datasource of terraform in cloudformation as of now. You could also fashion something using cloudformation macros but that would be a bit convulted.
Finally I made a vpc stack with cloudformation, it gets parameter input from aws cli and output the vpc id, subnet id, etc.
This cloudformation template has only one null resource (because cloudformation will report error if no resource in its template).
Description: >
This template deploys a VPC, with a pair of public and private subnets spread
across two Availabilty Zones. It deploys an Internet Gateway, with a default
route on the public subnets. It deploys a pair of NAT Gateways (one in each AZ),
and default routes for them in the private subnets.
Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
VPC:
Description: Please enter the VPC ID
Type: String
VpcCIDR:
Description: Please enter the IP range (CIDR notation) for this VPC
Type: String
PublicSubnet1:
Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone
Type: String
PublicSubnet2:
Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone
Type: String
PrivateSubnet1:
Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone
Type: String
PrivateSubnet2:
Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone
Type: String
Conditions:
HasNot: !Equals [ 'true', 'false' ]
Resources:
NullResource:
Type: 'Custom::NullResource'
Condition: HasNot
Outputs:
VPC:
Description: A reference to the created VPC
Value: !Ref VPC
Export:
Name: !Sub "${EnvironmentName}:VPC"
PublicSubnet1:
Description: A reference to the public subnet in the 1st Availability Zone
Value: !Ref PublicSubnet1
Export:
Name: !Sub "${EnvironmentName}:PublicSubnet1"
PublicSubnet2:
Description: A reference to the public subnet in the 2nd Availability Zone
Value: !Ref PublicSubnet2
Export:
Name: !Sub "${EnvironmentName}:PublicSubnet2"
PrivateSubnet1:
Description: A reference to the private subnet in the 1st Availability Zone
Value: !Ref PrivateSubnet1
Export:
Name: !Sub "${EnvironmentName}:PrivateSubnet1"
PrivateSubnet2:
Description: A reference to the private subnet in the 2nd Availability Zone
Value: !Ref PrivateSubnet2
Export:
Name: !Sub "${EnvironmentName}:PrivateSubnet2"
VpcCIDR:
Description: VPC CIDR
Value: !Ref VpcCIDR
Export:
Name: !Sub "${EnvironmentName}:VpcCIDR"
I run a bash script to collect these data (you can write javascript, pytyon or any other languages to collect these data), feed them to above cloudformation as parameters.
#!/bin/bash
set -ex
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )"
# collect the vpc details.
# you can run aws cli or any aws sdk to collect them.
source ../common_function.sh
echo ${VPC_ID}
aws --profile "${AWS_PROFILE}" --region "${AWS_DEFAULT_REGION}" \
cloudformation deploy \
--stack-name "${ENVIRONMENT_NAME}-vpc" \
--capabilities CAPABILITY_IAM \
--template-file "${DIR}/vpc.yaml" \
--parameter-overrides \
EnvironmentName="${ENVIRONMENT_NAME}" \
VPC="${VPC_ID}" \
VpcCIDR="${VPC_CIDR}" \
PublicSubnet1="${PUBLIC_SUBNET_ID_1}" \
PublicSubnet2="${PUBLIC_SUBNET_ID_2}" \
PrivateSubnet1="${PRIVATE_SUBNET_ID_1}" \
PrivateSubnet2="${PRIVATE_SUBNET_ID_2}"
After you deploy above cfn stack, you can reference these output variables in other cfn stacks.
VpcId:
'Fn::ImportValue': !Sub "${EnvironmentName}:VPC"

Transforming a CommaDelimitedList of Roles to list of Arns in Cloudformation

I have a cloudformation template generating a kms key with a policy document to grant roles access to the key. Now I want the roles to be a CommaDelimitedList Parameter of the Cloudformation template and I do not know the size in advanced. So I have input like this:
["role1", "role2", ...]
and have to generate this:
Principal:
AWS:
- !Sub "arn:aws:iam::${AWS::AccountId}:role/role1",
- !Sub "arn:aws:iam::${AWS::AccountId}:role/role2",
...
Is this transformation possible in cloudformation?
Not possible.
What you need to do is to pass the ARNs list. For example:
SomeParam:
"Fn::Join":
- ","
-
- !GetAtt "role1.Arn"
- !GetAtt "role2.Arn"
And just use it directly, CommaDelimitedList is automatically transformed into list by CloudFormation when passed as a parameter:
Principal:
AWS: !Ref "RolesParameter"
If you have just role names, you need to build the ARNs on your own, like in your question, but before passing as an argument:
SomeParam:
"Fn::Join":
- ","
-
- !Sub "arn:aws:iam::${AWS::AccountId}:role/role1"
- !Sub "arn:aws:iam::${AWS::AccountId}:role/role2"