How can I restrict a Cloudformation template to a particular account. Can I restrict or have the Cloudformation to validate the AWS Account number with the one that is hard coded? Are there any way?
You could create a CloudFormation Condition that checks the value of AWS::AccountId.
Then, reference the condition from each resource so that the resource is only created if the Condition is true.
Does this work?
Parameters:
AccNo:
Type: Number
Description: Enter the number to be validated
Conditions:
Number: !Equals [ !Ref AccNo, AWS::AccountId ]
Resources:
S3Bucket:
Condition: Number
Type: AWS::S3::Bucket
DeletionPolicy: Retain
Related
I am trying to create Amazon RDS instances in different environments using CloudFormation templates. There is a Multi-AZ requirement in Prod, but other environments do not need Multi-AZ. This calls for a condition function in CloudFormation.
Based on the RDS CloudFormation docs and using the if condition in CloudFormation, the following should work in the template:
Conditions:
IsProd: !Equals [ !Ref EnvironmentType, prod ]
...
Resources:
MyRDSInstance:
Properties:
...
AvailabilityZone:
!If [ IsProd, AWS::NoValue, af-south-1a ]
...
MultiAZ: !If [ IsProd, true, false ]
When IsProd evaluates to:
false, AvailabilityZone: af-south-1a and MultiAZ: false
true, AvailabilityZone is removed and MultiAZ: true, which meets the requirement specified in the docs:
You can't set the AvailabilityZone parameter if the MultiAZ parameter is set to true.
However, when trying to deploy the prod RDS instance, I still get the following error in CloudFormation when creating the stack, which then prevents the resources from being created at all:
Requesting a specific availability zone is not valid for Multi-AZ instances. (Service: AmazonRDS; Status Code: 400; Error Code: InvalidParameterCombination; Request ID: e6177fe4-4a4b-4db3-ba66-5f0e0f7218eb; Proxy: null)
I suspect this is a bug in AWS due to a recent change that was applied in the source code, even though it was related to the CDK and not CloudFormation:
Issue: Availability Zone parameter silently removed from stack when MultiAZ is true
Fix committed on May 25, 2021: fix(rds): Add exception throw when az is defined for multi-az db inst…. I am getting the error thrown in this exact fix.
Could it be that CloudFormation is now not making provision for the AWS::NoValue pseudo parameter? If this is a bug in the source code, is there any way to get around this so I can still achieve Multi-AZ in the prod environment only?
So I tried to replicate the same at my end but in my case I was able to successfully create the RDS resource. I am attaching the template which I used for your reference.
AWSTemplateFormatVersion: 2010-09-09
Description: >-
Description": "AWS CloudFormation Sample Template for creating an Amazon RDS DB instance:
Sample template showing how to create a DB instance with Enhanced Monitoring enabled.
**WARNING** This template creates an RDS DB instance. You will be billed for the AWS
resources used if you create a stack from this template.
Parameters:
IsMultiAZ:
Type: String
Default: false
AllowedValues: [true,false]
Description: Please enter either "true" or "false"
DBInstanceID:
Default: mydbinstance
Description: My database instance
Type: String
MinLength: '1'
MaxLength: '63'
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: >-
Must begin with a letter and must not end with a hyphen or contain two
consecutive hyphens.
DBName:
Default: mydb
Description: My database
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.
DBInstanceClass:
Default: db.m5.large
Description: DB instance class
Type: String
ConstraintDescription: Must select a valid DB instance type.
DBAllocatedStorage:
Default: '50'
Description: The size of the database (GiB)
Type: Number
MinValue: '20'
MaxValue: '65536'
ConstraintDescription: must be between 20 and 65536 GiB.
DBUsername:
NoEcho: 'true'
Description: Username for MySQL database access
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: Password MySQL database access
Type: String
MinLength: '8'
MaxLength: '41'
AllowedPattern: '[a-zA-Z0-9]*'
ConstraintDescription: must contain only alphanumeric characters.
Conditions:
CheckIsMultiZone:
!Equals [!Ref IsMultiAZ, true]
Resources:
MyDB:
Type: 'AWS::RDS::DBInstance'
Properties:
DBInstanceIdentifier: !Ref DBInstanceID
DBName: !Ref DBName
DBInstanceClass: !Ref DBInstanceClass
AllocatedStorage: !Ref DBAllocatedStorage
Engine: MySQL
EngineVersion: "8.0.16"
MasterUsername: !Ref DBUsername
MasterUserPassword: !Ref DBPassword
MultiAZ: !Ref IsMultiAZ
AvailabilityZone: !If [CheckIsMultiZone, !Ref AWS::NoValue, "us-east-1a"]
As you can see I have used the same concept you used. Can you test this template at your end to see if this is working or not. One issue I found in your template is that you are using AWS::NoValue while the correct format is !Ref AWS::NoValue as shown in my template. I believe this is the issue in your case. You can check the example here .
Is it possible to create a CloudFormation template that takes the IAM role's ARN as the input and updates its policy assigned to it to add more privileges?I tried to work with the CloudFormation designer but it is very confusing and not straightforward,
Yes, you can do this with a template such as this:
Description: Add policy to existing role
Parameters:
MyExistingRoleName:
Type: String
Description: Name of the Role you want to add a policy to
Resources:
MyNewPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: "my-new-policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- "s3:ListAllMyBuckets"
Resource:
- "*"
Roles:
- !Ref MyExistingRoleName
While developing a CloudFormation template, I am following the principle of least privilege. So I am providing CloudFormation a role to assume, and which has the minimal set of privileges.
The template contains an AWS::AutoScaling::AutoScalingGroup which is based on a AWS::EC2::LaunchTemplate:
ECSAutoScalingGroup:
DependsOn: ECSCluster
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
VPCZoneIdentifier: !Ref Subnets
LaunchTemplate:
LaunchTemplateId: !Ref ECSLaunchTemplate
Version: !GetAtt ECSLaunchTemplate.LatestVersionNumber
MinSize: 1
MaxSize: 2
DesiredCapacity: 1
...
ECSLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Metadata:
AWS::CloudFormation::Init:
...
Properties:
LaunchTemplateName: test-template
LaunchTemplateData:
...
When I create a CloudFormation stack out of this template, I get the following error on the ECSAutoScalingGroup resource:
Failed to retrieve attribute [LatestVersionNumber] for resource
[ECSLaunchTemplate]: You are not authorized to perform this operation.
(Service: AmazonEC2; Status Code: 403; Error Code:
UnauthorizedOperation; Request ID:
e0f01fd0-ee2a-4260-94f4-3c65177d05ee; Proxy: null)
Which IAM policy should I add to the IAM Role which is assumed by CloudFormation? Clearly, if I give it AdministratorAccess, it succeeds. However, I would like to follow the principal of least privilege.
Any ideas?
Thanks.
Answering my own question here. One should add these 2 actions to their IAM Role policy:
ec2:DescribeLaunchTemplates
ec2:DescribeLaunchTemplateVersions
I have two conditions in my CloudFormation template which are used to choose which resource to deploy based on environment. However when referencing the resource later in the template CloudFormation throws "Unresolved resource dependencies" error. Any idea whats wrong?
Template is shortened for readability. It is working fine without conditions.
Parameters:
EnvironmentName:
Type: String
Conditions:
IsProduction: !Equals [!Ref EnvironmentName, production]
IsNotProduction: !Not [!Equals [!Ref EnvironmentName, production]]
Resources:
Lambda:
Type: AWS::Serverless::Function
Condition: IsNotProduction
Lambda:
Type: AWS::Serverless::Function
Condition: IsProduction
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /aws/lambda/${Lambda}
So since the template is shortened, it's not that easy to tell what causes this error.
One thing that is problematic though, is that both of your Lambdas are called "Lambda". That won't work, you need to call them differently in your resources section.
I want to create a cloudformation stackset with resources like IAM and lambda in different regions. when I tried to deploy these resources, it failed because IAM roles are global and it is trying to create again in second region and whole stackset is failed.
Is there anyway I can mention the stackset to deploy GLobal Resources in one region and resources like lambda in all other regions?
Is there anyway I can mention the stackset to deploy GLobal Resources in one region and resources like lambda in all other regions?
Sadly there is not. You have to split your template, so that global resource are created as normal regional stacks.
I went through many resources and finally found a solution. If we split the template in stacksets then my dependent resources will break because creation is parallel in cloudformation. i.e. before global role gets created, lambda will try to get deployed and it will fail because the role is not available(required by lambda).
Hence we can add a condition to each of the global resources like below
Conditions:
RegionCheck: !Equals
- !Ref "AWS::Region"
- us-east-1
And, add the condition in the resources section as below,
Resources:
GlobalRolelambda:
Type: 'AWS::IAM::Role'
Condition: RegionCheck
Properties:
RoleName: !Ref LambdaExecutionRole
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/ReadOnlyAccess'
- 'arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- 'sts:AssumeRole'
Policies:
- PolicyName: lambda-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'glue:GetConnections'
- 'mediastore:ListContainers'
- 'mediastore:GetContainerPolicy'
Resource: '*'
But, after doing this, the problem would still exist because, if you add lambda resource with depends on attribute, role would get created in one region but not in the second region, lambda will fail to create in second region. We need to add a wait condition in the template to handle this as below Conditions:
CreateLambdaRole: !Equals [ !Ref LambdaRoleName, 'false' ]
CreateLamdaRoleRegion: !And
- !Condition RegionCheck
- !Condition CreateLambdaRole
and, add below resources after Role Resource,
CreateRoleWaitHandle:
Condition: CreateLamdaRoleRegion
DependsOn: GlobalRolelambda
Type: "AWS::CloudFormation::WaitConditionHandle"
#added, since DependsOn: !If is not possible, trigger by WaitCondition if CreateLamdaRoleRegion is false
WaitHandle:
Type: "AWS::CloudFormation::WaitConditionHandle"
#added, since DependsOn: !If is not possible
WaitCondition:
Type: "AWS::CloudFormation::WaitCondition"
Properties:
Handle: !If [CreateLamdaRoleRegion, !Ref CreateRoleWaitHandle, !Ref WaitHandle]
Timeout: "1"
Count: 0
and now, refer this in lambda resource,
lambdaProcessorFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: Lambda-processor
Description: ''
Handler: index.handler
Role:
Fn::Sub: 'arn:aws:iam::${AWS::AccountId}:role/LambdaExecutionRole'
Runtime: python3.6
Timeout: 600
MemorySize: 1024
Code:
S3Bucket: !Ref SourceBucketName
S3Key: !Ref SourceBucketKey
DependsOn: WaitCondition
Refer to the below source links, which might help
https://garbe.io/blog/2017/07/17/cloudformation-hacks/
CloudFormation, apply Condition on DependsOn