Does Amazon CloudFormation make provision for deploying RDS instances to different environments in a Multi-AZ configuration? - aws-cloudformation

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 .

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.

Getting error while validating aws cloud formation

I am trying to learn and practise AWS Cloudformation template.
While validating the template i am getting below error.
$ aws cloudformation validate-template --template-body file:///home/bhemanth/Downloads/ec2-templates/singe-instance-v2.yaml
An error occurred (ValidationError) when calling the ValidateTemplate operation: Invalid template resource property 'BlockDeviceMappings'
CloudFormation Template Code Error:
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CentOS EC2 Instance template'
Parameters:
KeyName:
Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
Type: AWS::EC2::KeyPair::KeyName
Default: hemanth
AllowedValues:
- hemanth
- client
ConstraintDescription: must be the name of an existing EC2 KeyPair.
InstanceType:
Description: CentOS
Type: String
Default: t2.small
AllowedValues:
- t2.micro
- t2.small
- t2.medium
ConstraintDescription: must be a valid EC2 instance type.
SSHLocation:
Description: The IP address range that can be used to SSH to the EC2 instances
Type: String
MinLength: '9'
MaxLength: '18'
Default: 0.0.0.0/0
AllowedPattern: "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})"
ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
Resources:
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType:
Ref: InstanceType
SecurityGroups:
- Ref: InstanceSecurityGroup
KeyName:
Ref: KeyName
ImageId: ami-01ed306a12b7d1c96
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: EnableAll
GroupDescription: Enable SSH access for all ports
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '0'
ToPort: '65535'
CidrIp:
Ref: SSHLocation
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
DeleteOnTermination: true
Status: attached
Hypervisor: xen
RootDeviceName: /dev/sda1
RootDeviceType: ebs
Tags:
- Key: Name
Value: Docker
VirtualizationType: hvm
UserData:
Fn::Base64: !Sub |
#!/usr/bin/env bash
yum install -y wget
wget -O- https://get.docker.com/ | sh
systemctl status docker
systemctl start docker
systemctl enable docker
systemctl status docker
systemctl status -l docker
Volumes:
- Attachments:
Device: /dev/sda1
State: attached
DeleteOnTermination: true
AvailabilityZone: us-west-2a
Encrypted: false
Size: 30
State: in-use
Iops: 100
VolumeType: gp2
Outputs:
InstanceId:
Description: InstanceId of the newly created EC2 instance
Value:
Ref: EC2Instance
AZ:
Description: Availability Zone of the newly created EC2 instance
Value:
Fn::GetAtt:
- EC2Instance
- AvailabilityZone
PublicDNS:
Description: Public DNSName of the newly created EC2 instance
Value:
Fn::GetAtt:
- EC2Instance
- PublicDnsName
PublicIP:
Description: Public IP address of the newly created EC2 instance
Value:
Fn::GetAtt:
- EC2Instance
- PublicIp
I am trying to prepare aws cloudformation template which will install docker from userdata and delete the volume when instance is terminated.
Can you please advice what is wrong in my template.
If possible can you please good tips and cheats for creating aws cloudformation for beginners.
Thanks,
Hemanth.
The CloudFormation Linter catches this and more with:
E3001 Invalid resource attribute BlockDeviceMappings for resource InstanceSecurityGroup
singe-instance-v2.yaml:51:5
E3001 Invalid resource attribute Hypervisor for resource InstanceSecurityGroup
singe-instance-v2.yaml:56:5
E3001 Invalid resource attribute RootDeviceName for resource InstanceSecurityGroup
singe-instance-v2.yaml:57:5
E3001 Invalid resource attribute RootDeviceType for resource InstanceSecurityGroup
singe-instance-v2.yaml:58:5
E3001 Invalid resource attribute Tags for resource InstanceSecurityGroup
singe-instance-v2.yaml:59:5
E3001 Invalid resource attribute VirtualizationType for resource InstanceSecurityGroup
singe-instance-v2.yaml:62:5
E3001 Invalid resource attribute UserData for resource InstanceSecurityGroup
singe-instance-v2.yaml:63:5
E3001 Invalid resource attribute Volumes for resource InstanceSecurityGroup
singe-instance-v2.yaml:73:5
Property types like BlockDeviceMappings, Tags, UserData, Volumes, and AvailabilityZone should be indented a level further than Properties:
I also believe those properties should be underneath Properties: in the AWS::EC2::Instance resource, because most of them are not valid property types of AWS::EC2::SecurityGroup
I don't think Hypervisor is a valid property type of any resource type, so I'm not sure where that property type came from
I'd recommend referencing the documentation for the AWS::EC2::SecurityGroup and AWS::EC2::Instance resource types

Cloudformation build stuck at "create in progress" - trying to add it to use specific SG

So the last item I added to this template was the attempt to have it use a particular SecurityGroup. I did not want it to create a new one. When I do the validate check that comes back ok but apparently my code is still not correct. Other that the template was working ok.
I have tried all I can think of. there is no error when i finally times out other than "internal error" so I am at a loss here.
Parameters:
VPC:
Description: Testing using this VPC
Type: String
Default: vpc-02765
SecGroup:
Description: Name of security group
Type: AWS::EC2::SecurityGroup
KeyName:
Description: Name of an existing EC2 key pair for SSH access to the EC2 instance.
Type: AWS::EC2::KeyPair::KeyName
InstanceType:
Description: EC2 instance type.
Type: String
Default: t2.micro
...
...
...
Resources:
EC2Instance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref 'InstanceType'
SubnetId: subnet-08b
KeyName: !Ref 'KeyName'
SecurityGroupIds:
- !Ref SecGroup
ImageId: !FindInMap
- AWSRegionArch2AMI
- !Ref 'AWS::Region'
- HVM64
'''
all I am trying to do is use the items I listed in the template. the vpc,securitygroup. The last time this worked was when I had the code in the template that builds a new SG. I than changed my mind and want to use an existing SG. so somewhere I messed up
This works in my templates:
Parameters:
SecGroup:
Type: AWS::EC2::SecurityGroup::Id
...
Resources:
MyInstance:
Properties:
SecurityGroupIds:
- !Ref SecGroup

Cloudformation error: Value of property NetworkInterfaces must be a list of objects

I get the error Value of property NetworkInterfaces must be a list of objects when referring to a NetworkInterface in a CloudFormation template.
Here is the relevant section:
MyAppNetworkInterface:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: !Ref SubnetPrivate
MyApp:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.medium
NetworkInterfaces:
- !Ref MyAppNetworkInterface
You can actually refer the Network Interface directly from the EC2 Host. But the syntax is slightly different:
MyAppNetworkInterface:
Type: AWS::EC2::NetworkInterface
Properties:
SubnetId: !Ref SubnetPrivate
MyApp:
Type: AWS::EC2::Instance
Properties:
InstanceType: t2.medium
NetworkInterfaces:
- NetworkInterfaceId: !Ref MyAppNetworkInterface
DeviceIndex: 0
(see: http://docs.amazonaws.cn/en_us/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-network-interface.html#cfn-awsec2networkinterface-templateexamples)
You can't do it that way. Instead , create the two resources independently, then connect with a network interface attachment resource.
http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-network-interface-attachment.html

Output a list in cloud formation

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.