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
Related
I need some guidance on cloudformation templates.
I have a stack called test1.yaml, there i created an IAM role called S3Role.
Now I have another stack called test2.yaml, there i created a managed policy to attach to existing iam role.
Now i want to call test1.yml file S3Role in test2.yml file of managed policy.
Can anyone help me with the script?
Can anyone help me with the script.
Obviously due to lack of details in your question, its not possible to provide any script. But I can provide general psudo-code.
test1.yaml
You will have to export the S3Role Arn or Name
Resources:
S3Role:
Type: IAM::ROLE
<rest of role definition>
Outputs:
RoleArn:
Value: !GetAtt S3Role.Arn
Exports:
Name: RoleArn
test2.yml
You will have to import the role exported Arn (or name) from test1.yaml:
Resources:
SomeResouce:
Properties:
Role: !ImportValue RoleArn
Hope this helps.
You need export the role from stack 1 and then import it in stack 2
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-stack-exports.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
Providing the complete script for cross-referencing an AWS resource in CloudFormation template.
test1.yaml has an IAM role (logical ID: IAMRole) which we export through the Outputs block. Also notice that the indentation of Outputs block is same as that of Resources block.
The Outputs block serves many purposes. From the AWS Documentation
The optional Outputs section declares output values that you can
import into other stacks (to create cross-stack references), return in
response (to describe stack calls), or view on the AWS CloudFormation
console.
test1.yaml
Resources:
IAMRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: TrustPolicy
Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonS3FullAccess
Path: /
RoleName: IAMRole
Outputs:
ExportIAMRole:
Description: Export the IAMRole to use in test2.yaml
Value: !Ref IAMRole
Export:
Name: IAMRole
In test2.yaml we import the value by referencing the name we have given under Export in Outputs block.
test2.yaml
Resources:
IAMPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: IAMPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- kms:ListAliases
- kms:Encrypt
- kms:Decrypt
Resource: "*"
Roles:
- !ImportValue IAMRole
I am trying to use AWS CloudFormation Template to create an EC2 Instance with some userdata generated using dynamic references and cross-stack reference in the template . There is a parameter stored in AWS Systems Manager Parameter Store with Name:/MyCustomParameter and Value:Test1.
The idea is to pass a parameter to the template stack (Stack A) which refers to another cloudformation stack (StackB). Stack B exports a variable with reference "StackB::ParameterStoreName". Stack A uses Fn::ImportValue: 'StackB::ParameterStoreName' to get it's value so that it can be used with dynamic references method to get it's value from AWS SSM Parameter Store using {{resolve:ssm:/MyCustomParameter:1}} and pass it's value to the UserData field in the template. I am facing difficulties while trying to use nested Fn::Sub: function with this use-case.
I tried removing the | pipe and using double quotes with escaped new line character but that doesn't work.
I also tried using a different type of resource and it's properties where is worked. Below is an example of the code that worked.
Resources:
TestBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName:
Fn::Sub:
- '${SSMParameterValue}-12345'
- SSMParameterValue:
Fn::Sub:
- '{{resolve:ssm:${SSMParameterName}:1}}'
- SSMParameterName:
Fn::ImportValue:
!Sub '${CustomStack}::ParameterStoreName'
Below is an extract of the current code I have:
Parameters:
CustomStack:
Type: "String"
Default: "StackB"
Resources:
MyCustomInstance:
Type: 'AWS::EC2::Instance'
Properties:
UserData:
Fn::Base64:
Fn::Sub:
- |
#!/bin/bash -e
#
# Bootstrap and join the cluster
/etc/eks/bootstrap.sh --b64-cluster-ca '${SSMParameterValue}' --apiserver-endpoint '${Endpoint}' '${ClusterName}'"
- SSMParameterValue:
Fn::Sub:
- '{{resolve:ssm:/${SSMParameterName}:1}}'
- SSMParameterName:
Fn::ImportValue:
!Sub '${CustomStack}::ParameterStoreName'
Endpoint:
Fn::ImportValue:
!Sub '${CustomStack}::Endpoint'
ClusterName:
Fn::ImportValue:
!Sub '${CustomStack}::ClusterStackName'
Current Output:
#!/bin/bash -e
#
# Bootstrap and join the cluster
/etc/eks/bootstrap.sh --b64-cluster-ca `{{resolve:ssm:MyCustomParameter:1}}` --apiserver-endpoint 'https://04F1597P0HJ11FQ54K0YFM9P19.gr7.us-east-1.eks.amazonaws.com' 'eks-cluster-1'
Expected Output:
#!/bin/bash -e
#
# Bootstrap and join the cluster
/etc/eks/bootstrap.sh --b64-cluster-ca `Test1` --apiserver-endpoint 'https://04F1597P0HJ11FQ54K0YFM9P19.gr7.us-east-1.eks.amazonaws.com' 'eks-cluster-1'
I think it is because the resolve is in the base64, maybe...? When it processes the line it just sees a block of base64 and not the {{resolve...}} code. The "resolves" get processed at a later pass than the !Functions, because they can't be resolved until the code is running.
To work around it, I added a temporary SSM parameter :
eksCAtmp:
Type: "AWS::SSM::Parameter"
Properties:
Type: String
Value:
Fn::Join:
- ''
- - '{{resolve:ssm:'
- Fn::ImportValue:
!Sub "${ClusterName}-EksCA"
- ':1}}'
That imports the original SSM parameter and gets rid of the requirement to "import" and resolve it again. So now you can use !GetAtt eksCAtemp.Value
eg:
UserData: !Base64
"Fn::Sub":
- |
#!/bin/bash
set -o xtrace
/etc/eks/bootstrap.sh ${ClusterName} --b64-cluster-ca ${CA} --apiserver-endpoint ${endpoint} --kubelet-extra-args '--read-only-port=10255'
/opt/aws/bin/cfn-signal --exit-code $? \
--stack ${AWS::StackName} \
--resource NodeGroup \
--region ${AWS::Region}
- endpoint:
Fn::ImportValue:
!Sub "${ClusterName}-EksEndpoint"
CA: !GetAtt eksCAtmp.Value
(Of course if they allowed cross stack exports to be more than 1024 characters, we wouldn't need this for firing up EKS on a private network.)
You can write like below:
UserData:
Fn::Base64:
Fn::Sub:
- |
#!/bin/bash -e
#
# Bootstrap and join the cluster
export SSMParameterValue=$(aws --region ${AWS::Region} ssm get-parameters --names ${SSMParameterName} --query 'Parameters[0].Value' --output text)
/etc/eks/bootstrap.sh --b64-cluster-ca \`$SSMParameterValue\` --apiserver-endpoint '${Endpoint}' '${ClusterName}'"
- SSMParameterName:
Fn::ImportValue:
!Sub '${CustomStack}::ParameterStoreName'
Endpoint:
Fn::ImportValue:
!Sub '${CustomStack}::Endpoint'
Don't forget your EC2 role need ssm:GetParameters permission.
Since I had an issue of 200 resource error, I found a way of using cross stack reference by dividing into different services. I managed to do that by using the cross-stack reference. The issue is I cannot give the restApiId & restApiRootResourceId dynamically. Right now, am statically setting ids into the service-2.
Basically the service-1 looks like,
provider:
name: aws
runtime: nodejs8.10
apiGateway:
restApiId:
Ref: ApiGatewayRestApi
restApiResources:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
custom:
stage: "${opt:stage, self:provider.stage}"
resources:
Resources:
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:service}-${self:custom.stage}-1
Outputs:
ApiGatewayRestApiId:
Value:
Ref: ApiGatewayRestApi
Export:
Name: ApiGatewayRestApi-restApiId
ApiGatewayRestApiRootResourceId:
Value:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
Export:
Name: ApiGatewayRestApi-rootResourceId
And the service-2 looks like this,
provider:
name: aws
runtime: nodejs8.10
apiGateway-shared:
restApiId:
'Fn::ImportValue': ApiGatewayRestApi-restApiId
restApiRootResourceId:
'Fn::ImportValue': ApiGatewayRestApi-rootResourceId
As the above service-2 config, I cannot reference the Ids.
FYI: Both services are in different files.
So How what's wrong with this approach?
Serverless has special syntax on how to access stack output variables: {cf:stackName.outputKey}.
Note that using the Fn::ImportValue would work inside the resources section.
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"
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.