Photo by Philip Myrtorp on Unsplash
In a world where changes are best done automatically, it is important to have the right set of tools for this. Automation helps improve your level of operational efficiency exponentially.
There is much debate about the best IaC (Infrastructure as Code) provider. Some of the most popular options are Terraform, Ansible, and CloudFormation. All of them have pros and cons, but some of the underlying themes of all IaC providers include:
Maintaining Infrastructure in a desired state
Consistent deployment of resources
Significantly higher level of scalability and velocity
Adherence to company policy for resources
If your resources are mostly on AWS, then it makes sense to use CloudFormation. In a nutshell, CloudFormation templates are just JSON (or YAML) files that describe how you want your resources deployed.
So let’s look at the anatomy of a template to better understand how they allow us to have much better control over our cloud infrastructure.
Below, I have laid out a sample template designed to install the CloudWatch Agent on amazon linux. We will talk more about what each section is and does.
---
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Template to install CloudWatchAgent on amazon linux. It was validated on amazon linux 2'
Parameters:
KeyName:
Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
Type: AWS::EC2::KeyPair::KeyName
ConstraintDescription: must be the name of an existing EC2 KeyPair.
InstanceType:
Description: EC2 instance type
Type: String
Default: m4.2xlarge
ConstraintDescription: must be a valid EC2 instance type.
InstanceAMI:
Description: Managed AMI ID for EC2 Instance
Type : String
Default: ami-7707a10f
IAMRole:
Description: EC2 attached IAM role
Type: String
Default: CloudWatchAgentAdminRole
ConstraintDescription: must be an existing IAM role which will be attached to EC2 instance.
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
Metadata:
AWS::CloudFormation::Init:
configSets:
default:
- 01_setupCfnHup
- 02_config-amazon-cloudwatch-agent
- 03_restart_amazon-cloudwatch-agent
UpdateEnvironment:
- 02_config-amazon-cloudwatch-agent
- 03_restart_amazon-cloudwatch-agent
# Definition of json configuration of AmazonCloudWatchAgent, you can change the configuration below.
02_config-amazon-cloudwatch-agent:
files:
'/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json':
content: !Sub |
{
"metrics":{
"append_dimensions":{
"AutoScalingGroupName":"${!aws:AutoScalingGroupName}",
"ImageId":"${!aws:ImageId}",
"InstanceId":"${!aws:InstanceId}",
"InstanceType":"${!aws:InstanceType}"
},
"metrics_collected":{
"mem":{
"measurement":[
"mem_used_percent"
]
},
"swap":{
"measurement":[
"swap_used_percent"
]
}
}
}
}
# Invoke amazon-cloudwatch-agent-ctl to restart the AmazonCloudWatchAgent.
03_restart_amazon-cloudwatch-agent:
commands:
01_stop_service:
command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a stop
02_start_service:
command: /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json -s
# Cfn-hup setting, it is to monitor the change of metadata.
# When there is change in the contents of json file in the metadata section, cfn-hup will call cfn-init to restart the AmazonCloudWatchAgent.
01_setupCfnHup:
files:
'/etc/cfn/cfn-hup.conf':
content: !Sub |
[main]
stack=${AWS::StackId}
region=${AWS::Region}
interval=1
mode: '000400'
owner: root
group: root
'/etc/cfn/hooks.d/amazon-cloudwatch-agent-auto-reloader.conf':
content: !Sub |
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.EC2Instance.Metadata.AWS::CloudFormation::Init.02_config-amazon-cloudwatch-agent
action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource EC2Instance --region ${AWS::Region} --configsets UpdateEnvironment
runas=root
mode: '000400'
owner: root
group: root
"/lib/systemd/system/cfn-hup.service":
content: !Sub |
[Unit]
Description=cfn-hup daemon
[Service]
Type=simple
ExecStart=/opt/aws/bin/cfn-hup
Restart=always
[Install]
WantedBy=multi-user.target
commands:
01enable_cfn_hup:
command: !Sub |
systemctl enable cfn-hup.service
02start_cfn_hup:
command: !Sub |
systemctl start cfn-hup.service
Properties:
InstanceType:
Ref: InstanceType
IamInstanceProfile:
Ref: IAMRole
KeyName:
Ref: KeyName
ImageId:
Ref: InstanceAMI
SecurityGroups:
- Ref: InstanceSecurityGroup
UserData:
# This script below is to install AmazonCloudWatchAgent, restart AmazonCloudWatchAgent and tell the result to cloudformation.
Fn::Base64: !Sub |
#!/bin/bash
rpm -Uvh https://s3.amazonaws.com/amazoncloudwatch-agent/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm
/opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource EC2Instance --region ${AWS::Region} --configsets default
/opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackId} --resource EC2Instance --region ${AWS::Region}
CreationPolicy:
ResourceSignal:
Count:1
Timeout:"PT15M"
InstanceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable SSH access via port 22
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp:
Ref: SSHLocation
Format Version:
This is the CloudFormation template version that the template conforms to. I generally haven’t ever had to mess with this and as you can see the versions don’t change too often.
Description
This is a text string that describes the template. Useful to say what the purpose of this template is, the only requirement here is that it follows the version section.
Parameters
Our first very important section, these are sets of values that you want to pass to your template at runtime. You can refer to parameters in the Resources and Outputs section. For example, you can say what type of EC2 instance you want created.
Resources
This is the only required section of a template. It specifies your stack resources and their properties, such as instances or buckets. You can refer to resources in the Resources and Outputs section.
Outputs
Describes the values that are returned whenever you view your stacks properties. Mostly used to view values via the CLI.
Conditions
Conditional statements determine whether certain resources are created or whether certain resource properties are assigned a value during stack creation/update. For example, you could deny creation of IAM users if MFA is not enabled.
Metadata
Objects that provide further information about a template, or resources.
These are some core components to CloudFormation templates. If you want to take a peek at more templates, AWS has a GitHub repo full of them you can browse through here.
I hope this is helpful for you if you are looking to understand more about IaC, and CloudFormation in particular. If DevOps is something that interests you, then you should become quite familiar with deploying Infrastructure as Code.
Comentarios