AWS Cloudformation create resource conditionally - amazon-web-services

I was looking at the Condition Function Fn::If: to create or provision a resource only if a condition is evaluated to true. In my case, created a policy if the environment is prod.
Parameters:
Env:
Description: Environment
Type: String
Conditions:
IsProd: !Equals [!Ref Env, 'prod']
I know how to do it for a property, but not for the entire resource block.
Type: 'AWS::IAM::Policy'
Properties:
PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
Roles:
- !Ref RootRole
Is this something possible?

You can do it using Condition: resource attribute. For example:
Resources:
MyIAMPolicy:
Condition: IsProd
Type: 'AWS::IAM::Policy'
Properties:
PolicyName: root
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: '*'
Resource: '*'
Roles:
- !Ref RootRole
More on this can be found here:
Conditionally launch AWS CloudFormation resources based on user input

Related

how to add a condition when writing a aws policy via cloudformation?

I am creating some IAM roles, policies via cloudformation but I would like to add policies based on the condition I have, say if it is dev then i would like to add certain policy statement. any suggestions ?
Parameters:
environment:
Type: String
Default: dev
AllowedValues:
- dev
- prd
Condition:
isDev: !Equals [ !Ref environment, dev]
Resources:
StandAlonePolicy:
Type: AWS::IAM::Policy
Properties:
#How to add a condition - isDev
PolicyName: "s3-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource: "*"
Action:
- "s3:Get*"
You can do this using If:
Parameters:
environment:
Type: String
Default: dev
AllowedValues:
- dev
- prd
Conditions:
isDev: !Equals [ !Ref environment, dev]
Resources:
StandAlonePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: "s3-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Resource: "*"
Action:
- "s3:Get*"
- !If
- isDev
- Sid: new-statement-for-dev-only
Effect: Allow
Resource: "*"
Action:
- "s3:Put*"
- !Ref "AWS::NoValue"

IAM Role via CloudFormation Error in Principle

I am trying to create an IAM role via cloudformation for an existing IAM user as principle and an existing dynamodb table. I have verified through yamllint and yaml is well-formatted and yet cloudformation is complaining about malformatted file.
Following is my cloudformation template:
AWSTemplateFormatVersion: 2010-09-09
Parameters:
vTableName:
Type: String
Description: the tablename
Default: arn:aws:dynamodb:ap-southeast-2:1234567:table/test-table
vUserName:
Type: String
Description: New account username
Default: mytestuser
Resources:
DynamoRoleForTest:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${vUserName}'
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: DynamoPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
- Action:
- dynamodb:BatchGet*
- dynamodb:DescribeStream
- dynamodb:DescribeTable
- dynamodb:Get*
- dynamodb:Query
- dynamodb:Scan
Resource: !Ref vTableName
and following is error I receive,when I try to create this template:
Syntax errors in policy. (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; Request ID: 237dd218-a2a2-4194-9063-104f8022cb80)
Thanks for any advice.
There is not-needed - in your Action:
- Action:
Therefore, your template should be (removed -):
AWSTemplateFormatVersion: 2010-09-09
Parameters:
vTableName:
Type: String
Description: the tablename
Default: arn:aws:dynamodb:ap-southeast-2:1234567:table/test-table
vUserName:
Type: String
Description: New account username
Default: mytestuser
Resources:
DynamoRoleForTest:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
AWS:
- !Sub 'arn:aws:iam::${AWS::AccountId}:user/${vUserName}'
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: DynamoPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- dynamodb:BatchGet*
- dynamodb:DescribeStream
- dynamodb:DescribeTable
- dynamodb:Get*
- dynamodb:Query
- dynamodb:Scan
Resource: !Ref vTableName
Principle is fine, assuming it exists.

Add statements to serverless "KeyPolicy" on KMS Resource

I have a serverless application, which creates a KMS Resource:
# serverless.yml 1
resources:
Resources:
SomeLambdaRole:
Type: AWS::IAM::Role
AnotherLambdaRole:
Type: AWS::IAM::Role
TheKey:
Type: AWS::KMS::Key
DeletionPolicy: Retain
Properties:
Description: The key
Enabled: true
KeyPolicy:
Version: '2012-10-17'
Statement:
- Sid: Allow use of the key
Effect: Allow
Principal:
AWS:
- Fn::GetAtt: [SomeLambdaRole, Arn]
- Fn::GetAtt: [AnotherLambdaRole, Arn]
Action:
- 'kms:Encrypt'
- 'kms:Decrypt'
- 'kms:ReEncrypt'
- 'kms:GenerateDataKey*'
Resource: '*'
From another serverless application where some roles are created, I want to give these new roles the same permissions that SomeLambdaRole and AnotherLambdaRole have on the "TheKey" Resource
# serverless.yml 2
resources:
Resources:
YetAnotherLambdaRole:
Type: AWS::IAM::Role
# Do something to let this role have the same permission as "SomeLambdaRole" and "AnotherLambdaRole" for the "TheKey" Resource
Is this possible or should I try another approach?

How could I output IAM role and use that in another stack?

I've two stacks called "createIAMRole", "createElasticSearch" and "createdLambda". What I want is I want to use IAM Role ARN from first stack called "createIAMRole" in both "createElasticSearch" and "createdLambda".
createIAMRole
AWSTemplateFormatVersion: '2010-09-09'
Description: >
blah.
Resources:
myIAMRole:
Type: AWS::IAM::Role
Properties:
..
..
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- dynamodb:*
Resource: "*"
PolicyName: "myIAMRolePolicy"
Outputs:
myIAMRole:
Description: myIAMRole to use Stacks
Value: !Ref myIAMRole
"createElasticSearch"
Resources:
ElasticsearchDomain:
Type: AWS::Elasticsearch::Domain
Properties:
..
..
AccessPolicies:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS:
- >>> THERE IS I WANT TO ADD <<<
Action: "es:*"
Resource: "*"
AdvancedOptions:
rest.action.multi.allow_explicit_index: "true"
Please let me know how to do it, thanks.
For the createIAMRole you need to export the output:
Outputs: myIAMRole:
Description: myIAMRole to use Stacks
Value: !Ref myIAMRole
Export:
Name: myIAMRole
And for the createElasticSearch you need to "ImportValue":
Fn::ImportValue:
!Sub "${myIAMStackName}-myIAMRole"
More Information: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-importvalue.html
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/outputs-section-structure.html

Cloudformation Template error: every Fn::GetAtt object requires two non-empty parameters

I have made a nested cloudformation stack that in this case references a Lambda child stack.
Because I have multiple LambdaFunctions, I designed the LambdaFunction resource in the Lambda child
template such that it can repeat the same actions across all the Lambda Functions specified in the Parent template.
However, I get the following error once I execute create-stack: Template error: every Fn::GetAtt object requires two non-empty parameters, the resource name and the resource attribute, which is pointing to the Lambda Child template.
I tried adding a
DependsOn clause in which I listed all the LambdaExecutionRoles, since the LambdaFunction references those, but that
didn't appear to resolve the issue. So something is either going wrong with taking in the LambdaName parameter
or grabbing the Arn. Any thoughts?
Portion of Parent template:
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
AlignmentLambdaFuncS3BucketName:
Type: String
AlignmentLambdaFuncS3KeyName:
Type: String
AlignmentLambdaFuncModuleName:
Type: String
HaploLambdaFuncS3BucketName:
Type: String
HaploLambdaFuncS3KeyName:
Type: String
HaploLambdaFuncModuleName:
Type: String
Resources:
AlignmentLambdaFunction:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
LambdaName: Alignment
BucketName: LambdaFuncS3BucketName
S3KeyName: LambdaFuncS3KeyName
ModuleName: LambdaFuncModuleName
TemplateURL: https://s3.amazonaws.com/CFNTemplate/lambda_resources.stack.yaml
TimeoutInMinutes: 1
HaploLambdaFunction:
Type: "AWS::CloudFormation::Stack"
Properties:
Parameters:
LambdaName: Haplo
BucketName: LambdaFuncS3BucketName
S3KeyName: LambdaFuncS3KeyName
ModuleName: LambdaFuncModuleName
TemplateURL: https://s3.amazonaws.com/CFNTemplate/lambda_resources.stack.yaml
TimeoutInMinutes: 1
Portion of Lambda child template:
AWSTemplateFormatVersion: '2010-09-09'
Description: lambda function and execution role stack.
Parameters:
LambdaName:
Type: String
BucketName:
Type: String
S3KeyName:
Type: String
ModuleName:
Type: String
KMSAdminUserARN:
Type: String
KMSEndUserARN:
Type: String
Resources:
LambdaFunction:
Type: "AWS::Lambda::Function"
Properties:
Handler: !Sub '${LambdaName}-{ModuleName}.handler'
Role:
Fn::GetAtt: [ !Sub '${LambdaName}LambdaExecutionRole', Arn ]
Code:
S3Bucket: !Sub '${LambdaName}{BucketName}'
S3Key: !Sub '${LambdaName}{S3KeyName}'
Runtime: "python3.6"
AlignmentLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: "sts:AssumeRole"
Policies:
- PolicyName: CanListBuckets
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:GetBucketLocation"
- "s3:ListAllMyBuckets"
Resource: "arn:aws:s3:::*"
- PolicyName: CanCallBatch
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "batch:*"
Resource: "*"
- PolicyName: CanLog
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:*
Resource: arn:aws:logs:*:*:*
HaploLambdaExecutionRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: "sts:AssumeRole"
Policies:
- PolicyName: CanListBuckets
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:GetBucketLocation"
- "s3:ListAllMyBuckets"
Resource: "arn:aws:s3:::*"
- PolicyName: CanCallBatch
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "batch:*"
Resource: "*"
- PolicyName: CanLog
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:*
Resource: arn:aws:logs:*:*:*
Unfortunately, you can't use any functions (for example, Sub) inside Fn::GetAtt's logical resource name:
For the Fn::GetAtt logical resource name, you cannot use functions. You must specify a string that is a resource's logical ID.
Source: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
+1 to divinedragon's solution. You should be able to abstract away the *ExecutionRole resources into a separate file, have that file output each resources arn, and then pick that up directly in the lambda template. Some pseudo code that has worked for me in the past (I use exports here, but you can just pass by parameter if you can't):
ExecutionRoleTempate.yml:
Resources:
ExecutionRole1:
Type: "AWS::IAM::Role"
...
ExecutionRole2:
Type: "AWS::IAM::Role"
...
Output:
ERArn1:
Value: Fn::GetAtt ExecutionRole1.arn
Export:
Name: ERArn1
ERArn2:
Value: Fn::GetAtt ExecutionRole2.arn
Export:
Name: ERArn2
lambda_resources.stack.yaml:
Resources:
LambdaFunction:
...
Role: Fn::ImportValue !Sub ....