One of the keys to working effectively with CloudFormation is knowing how to use the resource reference table on the AWS Resource and Property Types Reference. Here you can find a list of information and definitions of resources and properties.
AWSTemplateFormatVersion (optional): Indicates the version of the template. The latest version is 2010-09-09 and although it was released a long time ago, it is still used until now.
Description (optional) describes information related to the template.
Parameters (optional): allows you to add optional values to the template each time you need to create or update the stack.
Resources (required): specify which AWS resources you want to put on the stack for deployment, such as an Amazon EC2 instance or an Amazon S3 bucket.
Outputs (optional) defines output values that you can import into other stacks (to create cross-stack references), or as stack responses, or for display on AWS CloudFormation console.
For each parameter we must specify the name and type for the parameter. Supported parameter types include: String, Number, CommaDelimitedList, List, AWS-Specific Parameter and SSM Parameter. Below are some parameters we use in the workshop. These include
EC2InstanceType - defines the Instance type to be used in the template.
LatestAmiId - allows to automatically get the latest AMI ID and include it in the template.
SubnetID - lists all available Subnet IDs available in the region.
SourceLocation - specifies how to use regex in the AllowedPattern property to ensure that the input is appropriate, along with the MinLength and MaxLength values.
VPCID - lists available VPCs in the region.
AWSTemplateFormatVersion: "2010-09-09"
Description: "Deploy Single EC2 Linux Instance as part of MGT312 Workshop"
Parameters:
EC2InstanceType:
AllowedValues:
- t3.nano
- t3.micro
- t3.small
- t3.medium
- t3.large
- t3.xlarge
- t3.2xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
Default: t3.small
Description: Amazon EC2 instance type
Type: String
LatestAmiId:
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
SubnetID:
Description: ID of a Subnet.
Type: AWS::EC2::Subnet::Id
SourceLocation:
Description : The CIDR IP address range that can be used to RDP 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.
VPCID:
Description: ID of the target VPC (e.g., vpc-0343606e).
Type: AWS::EC2::VPC::Id
Resource: Security Group
And we have finished setting the parameters so that we can reuse CloudFormaton in the next time. Now we will go to define the required components, which are Resources.
It should be noted that proficient use of the AWS Resource and Property Types Reference documentation plays a very important role in creating a CloudFormation template.
The first resource we add is a Security Group with a rule that allows traffic on port 80. Here we use the *Intrinsic Function Ref *(Intrinsic Function Ref) technique to return the value of VPCID parameter.
AWS CloudFormation provides some built-in functionality that helps you manage Stacks and uses built-in functions in templates to assign values to properties that are only available at runtime.
Copy the following content and then paste it into the code in the New File window in the previous step:
Resources:
EC2InstanceSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: EC2 Instance Security Group
VpcId: !Ref 'VPCID'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref SourceLocation
Resource: Instance Role
In this step we will create a Role that we can assign to EC2 Instance, allowing it to send monitoring information and communicate with AWS Systems Manager Service (SSM). With that, an InstanceProfile is also created and references the Instance Role.
The code also uses the Intrinsic Function Sub technique to replace values in a string with Pseudo parameters like AWS::Region and AWS::Partition.
Dummy parameters are predefined parameters. We will not declare them in the template and use them like a normal parameter.
Copy the following content and then paste it into the code in the New File window in the previous step:
SSMInstanceRole:
Type : AWS::IAM::Role
Properties:
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- s3:GetObject
Resource:
- !Sub 'arn:aws:s3:::aws-ssm-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::amazon-ssm-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*'
- !Sub 'arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*'
Effect: Allow
PolicyName: ssm-custom-s3-policy
Path: /
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy'
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "ec2.amazonaws.com"
- "ssm.amazonaws.com"
Action: "sts:AssumeRole"
SSMInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Roles:
- !Ref SSMInstanceRole
Resource: EC2 Instance
Continue building the template with the EC2 Instance definition.
In this step all components including previously configured resources and parameters will be gathered together. We use the Intrinsic Function Reference !Ref technique to assign or create resources and values generated at runtime. Using !Ref also affects the order in which resources are generated by CloudFormation.
Copy the following content and then paste it into the code in the New File window in the previous step:
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: !Ref LatestAmiId
InstanceType: !Ref EC2InstanceType
IamInstanceProfile: !Ref SSMIstanceProfile
NetworkInterfaces:
- DeleteOnTermination: true
DeviceIndex: '0'
SubnetId: !Ref 'Subnet'
GroupSet:
- !Ref EC2InstanceSG
Tags:
- Key: "Name"
Value: "MGMT312-EC2"
Outputs
Finally, the Outputs content with the return result is the Private IP address of the Instance - a property value of the Resource - through the use of the GettAtt Intrinsic Function.
Refer to the AWS Resource and Property Types Reference documentation to find additional information that can be obtained with the GetAtt or !Ref function. Copy the following content and then paste it into the code in the New File window in the previous step:
Outputs:
EC2InstancePrivateIP:
Value: !GetAtt 'EC2Instance.PrivateIp'
Description: Private IP for EC2 Instances
AWSTemplateFormatVersion: "2010-09-09"
Description: "Deploy Single EC2 Linux Instance"
Parameters:
EC2InstanceType:
AllowedValues:
- t3.nano
- t3.micro
- t3.small
- t3.medium
- t3.large
- t3.xlarge
- t3.2xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
Default: t3.small
Description: Amazon EC2 instance type
Type: String
LatestAmiId:
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
SubnetID:
Description: ID of a Subnet.
Type: AWS::EC2::Subnet::Id
SourceLocation:
Description : The CIDR IP address range that can be used to RDP 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.
VPCID:
Description: ID of the target VPC (e.g., vpc-0343606e).
Type: AWS::EC2::VPC::Id
Resources:
EC2InstanceSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: EC2 Instance Security Group
VpcId: !Ref 'VPCID'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref SourceLocation
SSMInstanceRole:
Type : AWS::IAM::Role
Properties:
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Action:
- s3:GetObject
Resource:
- !Sub 'arn:aws:s3:::aws-ssm-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::aws-windows-downloads-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::amazon-ssm-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::amazon-ssm-packages-${AWS::Region}/*'
- !Sub 'arn:aws:s3:::${AWS::Region}-birdwatcher-prod/*'
- !Sub 'arn:aws:s3:::patch-baseline-snapshot-${AWS::Region}/*'
Effect: Allow
PolicyName: ssm-custom-s3-policy
Path: /
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore'
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/CloudWatchAgentServerPolicy'
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- "ec2.amazonaws.com"
- "ssm.amazonaws.com"
Action: "sts:AssumeRole"
SSMInstanceProfile:
Type: "AWS::IAM::InstanceProfile"
Properties:
Roles:
- !Ref SSMInstanceRole
EC2Instance:
Type: "AWS::EC2::Instance"
Properties:
ImageId: !Ref LatestAmiId
InstanceType: !Ref EC2InstanceType
IamInstanceProfile: !Ref SSMInstanceProfile
NetworkInterfaces:
- DeleteOnTermination: true
DeviceIndex: '0'
SubnetId: !Ref 'Subnet'
GroupSet:
- !Ref EC2InstanceSG
Tags:
- Key: "Name"
Value: "MGMT312-EC2"
Outputs:
EC2InstancePrivateIP:
Value: !GetAtt 'EC2Instance.PrivateIp'
Description: Private IP for EC2 Instances
cfn-lint singleec2instance.yaml
aws cloudformation create-stack --stack-name asg-cloudformation-stack --template-body file://singleec2instance.yaml --parameters ParameterKey=SubnetID,ParameterValue=subnet-04c111ad3987c5350 ParameterKey=VPCID,ParameterValue=vpc-0e3f81c05d3920198 --capabilities CAPABILITY_IAM --region us-east-1