Venkata Chitturi
DevOps Process and Tools
7 min readMay 2, 2017

--

Automate stack provisioning using Cloud Formation Vs Teraform

Cloud Formation is service from AWS and Teraform is similar service from Hashicorp Inc to design the whole infrastructure stack (Iaas)at once and define the every component in a stack and their dependencies.

Below Aws cloud formation template example the similar deploy of wordpress site using chef in aws.

Important concepts is aws is : parameters, properties, resources,version,mappings ,find in map functions. Detailed documentation on aws cloud formation template is avaiable in the below link: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/GettingStarted.html

{
“AWSTemplateFormatVersion” : “2010–09–09”,
“Description” : “AWS CloudFormation Sample Template for WordPress_Chef: WordPress is web software you can use to create a beautiful website or blog. This template installs a highly available, scalable WordPress deployment using a multi-AZ (Availability Zone) Amazon RDS database instance for storage. It demonstrates using the AWS CloudFormation bootstrap scripts to deploy the Chef client and using Chef-client in local mode to deploy WordPress. **WARNING** This template creates an Amazon EC2 instance, an Elastic Load Balancing load balancer, and an Amazon RDS database instance. You will be billed for the AWS resources used if you create a stack from this template.”,“Parameters” : {“KeyName”: {
“Description” : “Name of an existing EC2 key pair to enable SSH access to the instances”,
“Type”: “AWS::EC2::KeyPair::KeyName”,
“ConstraintDescription” : “must be the name of an existing EC2 KeyPair.”
},
“InstanceType” : {
“Description” : “Web Server EC2 instance type”,
“Type” : “String”,
“Default” : “t2.small”,
“AllowedValues” : [ “t1.micro”, “t2.nano”, “t2.micro”, “t2.small”, “t2.medium”, “t2.large”, “m1.small”, “m1.medium”, “m1.large”, “m1.xlarge”, “m2.xlarge”, “m2.2xlarge”, “m2.4xlarge”, “m3.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 for x.x.x.x/x.”
},
“DBClass” : {
“Description” : “Database instance class”,
“Type” : “String”,
“Default” : “db.t2.small”,
“AllowedValues” : [ “db.t1.micro”, “db.m1.small”, “db.m1.medium”, “db.m1.large”, “db.m1.xlarge”, “db.m2.xlarge”, “db.m2.2xlarge”, “db.m2.4xlarge”]
,
“ConstraintDescription” : “must select a valid database instance type.”
},
“DBName” : {
“Default”: “wordpressdb”,
“Description” : “The WordPress database nae”,
“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.”
},
“MultiAZDatabase”: {
“Default”: “false”,
“Description” : “Create a multi-AZ MySQL Amazon RDS database instance”,
“Type”: “String”,
“AllowedValues” : [ “true”, “false” ],
“ConstraintDescription” : “must be either true or false.”
},
“WebServerCapacity”: {
“Default”: “1”,
“Description” : “The initial nuber of web server instances”,
“Type”: “Number”,
“MinValue”: “1”,
“MaxValue”: “5”,
“ConstraintDescription” : “must be between 1 and 5 EC2 instances.”
},
“Mappings” : {
“AWSInstanceType2Arch” : {
“t1.micro” : { “Arch” : “PV64” },
“t2.nano” : { “Arch” : “HVM64” },
“t2.micro” : { “Arch” : “HVM64” },

},
“AWSInstanceType2NATArch” : {
“t1.micro” : { “Arch” : “NATPV64” },
“t2.nano” : { “Arch” : “NATHVM64” },
“t2.micro” : { “Arch” : “NATHVM64” },

}
,
“AWSRegionArch2AMI” : {
“us-east-1” : {“PV64” : “ami-2a69aa47”, “HVM64” : “ami-6869aa05”, “HVMG2” : “ami-61e27177”},
}
},“Conditions” : {
“Is-EC2-VPC” : { “Fn::Or” : [ {“Fn::Equals” : [{“Ref” : “AWS::Region”}, “eu-central-1” ]},
{“Fn::Equals” : [{“Ref” : “AWS::Region”}, “cn-north-1” ]}]},
“Is-EC2-Classic” : { “Fn::Not” : [{ “Condition” : “Is-EC2-VPC”}]}
},
“Resources” : {“ElasticLoadBalancer” : {
“Type” : “AWS::ElasticLoadBalancing::LoadBalancer”,
“Metadata” : {
“Comment1” : “Configure the Load Balancer with a simple health check and cookie-based stickiness”,
“Comment2” : “Use install path for healthcheck to avoid redirects — ELB healthcheck does not handle 302 return codes”
},
“Properties” : {
“AvailabilityZones” : { “Fn::GetAZs” : “” },
“CrossZone” : “true”,
“LBCookieStickinessPolicy” : [ {
“PolicyName” : “CookieBasedPolicy”,
“CookieExpirationPeriod” : “30”
} ],
“Listeners” : [ {
“LoadBalancerPort” : “80”,
“InstancePort” : “80”,
“Protocol” : “HTTP”,
“PolicyNames” : [ “CookieBasedPolicy” ]
} ],
“HealthCheck” : {
“Target” : “HTTP:80/wp-admin/install.php”,
“HealthyThreshold” : “2”,
“UnhealthyThreshold” : “5”,
“Interval” : “10”,
“Timeout” : “5”
}
}
},
“WebServerGroup” : {
“Type” : “AWS::AutoScaling::AutoScalingGroup”,
“Properties” : {
“AvailabilityZones” : { “Fn::GetAZs” : “” },
“LaunchConfigurationName” : { “Ref” : “LaunchConfig” },
“MinSize” : “1”,
“MaxSize” : “5”,
“DesiredCapacity” : { “Ref” : “WebServerCapacity” },
“LoadBalancerNames” : [ { “Ref” : “ElasticLoadBalancer” } ]
},
“CreationPolicy” : {
“ResourceSignal” : {
“Timeout” : “PT15M”
}
},
“UpdatePolicy”: {
“AutoScalingRollingUpdate”: {
“MinInstancesInService”: “1”,
“MaxBatchSize”: “1”,
“PauseTime” : “PT15M”,
“WaitOnResourceSignals”: “true”
}
}
},
“LaunchConfig”: {
“Type” : “AWS::AutoScaling::LaunchConfiguration”,
“Metadata” : {
“AWS::CloudFormation::Init” : {
“configSets” : {
“wordpress_install” : [“install_cfn”, “install_chefdk”, “install_chef”, “install_wordpress”, “run_chef”]
},
“install_cfn” : {
“files”: {
“/etc/cfn/cfn-hup.conf”: {
“content”: { “Fn::Join”: [ “”, [
“[main]\n”,
“stack=”, { “Ref”: “AWS::StackId” }, “\n”,
“region=”, { “Ref”: “AWS::Region” }, “\n”
]]},
“mode” : “000400”,
“owner” : “root”,
“group” : “root”
},
“/etc/cfn/hooks.d/cfn-auto-reloader.conf”: {
“content”: { “Fn::Join”: [ “”, [
“[cfn-auto-reloader-hook]\n”,
“triggers=post.update\n”,
“path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init\n”,
“action=/opt/aws/bin/cfn-init -v “,
“ — stack “, { “Ref” : “AWS::StackName” },
“ — resource LaunchConfig “,
“ — configsets wordpress_install “,
“ — region “, { “Ref” : “AWS::Region” }, “\n”
]]},
“mode” : “000400”,
“owner” : “root”,
“group” : “root”
}
},
“services” : {
“sysvinit” : {
“cfn-hup” : { “enabled” : “true”, “ensureRunning” : “true”,
“files” : [“/etc/cfn/cfn-hup.conf”, “/etc/cfn/hooks.d/cfn-auto-reloader.conf”] }
}
}
},
“install_chef” : {
“sources” : {
“/var/chef/chef-repo” : “http://github.com/opscode/chef-repo/tarball/master"
},
“files” : {
“/tmp/install.sh” : {
“source” : “https://www.opscode.com/chef/install.sh”,
“mode” : “000400”,
“owner” : “root”,
“group” : “root”
},
“/var/chef/chef-repo/.chef/knife.rb” : {
“content” : { “Fn::Join”: [ “”, [
“cookbook_path [ ‘/var/chef/chef-repo/cookbooks’ ]\n”,
“node_path [ ‘/var/chef/chef-repo/nodes’ ]\n”
]]},
“mode” : “000400”,
“owner” : “root”,
“group” : “root”
},

“commands” : {
“01_make_chef_readable” : {
“command” : “chmod +rx /var/chef”
},
“02_install_chef” : {
“command” : “bash /tmp/install.sh”,
“cwd” : “/var/chef”
},
“03_create_node_list” : {
“command” : “chef-client -z -c /var/chef/chef-repo/.chef/client.rb”,
“cwd” : “/var/chef/chef-repo”,
“env” : { “HOME” : “/var/chef” }
}
}
},
“install_chefdk” : {
“packages” : {
“rpm” : {
“chefdk” : “https://opscode-omnibus-packages.s3.amazonaws.com/el/6/x86_64/chefdk-0.2.0-2.el6.x86_64.rpm"
}
}
},
“run_chef” : {
“commands” : {
“01_run_chef_client” : {
“command” : “chef-client -z -c /var/chef/chef-repo/.chef/client.rb”,
“cwd” : “/var/chef/chef-repo”,
“env” : { “HOME” : “/var/chef” }
}
}
}
}
},
“Properties”: {
“ImageId” : { “Fn::FindInMap” : [ “AWSRegionArch2AMI”, { “Ref” : “AWS::Region” },
{ “Fn::FindInMap” : [ “AWSInstanceType2Arch”, { “Ref” : “InstanceType” }, “Arch” ] } ] },
“InstanceType” : { “Ref” : “InstanceType” },
“SecurityGroups” : [ {“Ref” : “WebServerSecurityGroup”} ],
“KeyName” : { “Ref” : “KeyName” },
“UserData” : { “Fn::Base64” : { “Fn::Join” : [“”, [
“#!/bin/bash -xe\n”,
“yum update -y aws-cfn-bootstrap\n”,
“/opt/aws/bin/cfn-init -v “,
“ — stack “, { “Ref” : “AWS::StackName” },
“ — resource LaunchConfig “,
“ — configsets wordpress_install “,
“ — region “, { “Ref” : “AWS::Region” }, “\n”,
“/opt/aws/bin/cfn-signal -e $? “,
“ — stack “, { “Ref” : “AWS::StackName” },
“ — resource WebServerGroup “,
“ — region “, { “Ref” : “AWS::Region” }, “\n”
]]}}
}
},
“DBSecurityGroup”: {
“Type”: “AWS::RDS::DBSecurityGroup”,
“Condition” : “Is-EC2-Classic”,
“Properties”: {
“DBSecurityGroupIngress”: {
“EC2SecurityGroupName”: { “Ref”: “WebServerSecurityGroup” }
},
“GroupDescription”: “database access”
}
},
“DBInstance” : {
“Type”: “AWS::RDS::DBInstance”,
“Properties”: {
“DBName” : { “Ref” : “DBName” },
“Engine” : “MySQL”,
“MultiAZ” : { “Ref”: “MultiAZDatabase” },
“MasterUsername” : { “Ref” : “DBUser” },
“DBInstanceClass” : { “Ref” : “DBClass” },
“AllocatedStorage” : { “Ref” : “DBAllocatedStorage” },
“MasterUserPassword”: { “Ref” : “DBPassword” },
“VPCSecurityGroups”: { “Fn::If” : [ “Is-EC2-VPC”, [ { “Fn::GetAtt”: [ “DBEC2SecurityGroup”, “GroupId” ] } ], { “Ref” : “AWS::NoValue”}]},
“DBSecurityGroups”: { “Fn::If” : [ “Is-EC2-Classic”, [ { “Ref”: “DBSecurityGroup” } ], { “Ref” : “AWS::NoValue”}]}
}
},
“WebServerSecurityGroup” : {
“Type” : “AWS::EC2::SecurityGroup”,
“Properties” : {
“GroupDescription” : “Enable HTTP access via port 80 locked down to the load balancer + SSH access”,
“SecurityGroupIngress” : [
{“IpProtocol” : “tcp”, “FromPort” : “80”, “ToPort” : “80”,
“SourceSecurityGroupOwnerId” : {“Fn::GetAtt” : [“ElasticLoadBalancer”, “SourceSecurityGroup.OwnerAlias”]},”SourceSecurityGroupName” : {“Fn::GetAtt” : [“ElasticLoadBalancer”, “SourceSecurityGroup.GroupName”]}},
{“IpProtocol” : “tcp”, “FromPort” : “22”, “ToPort” : “22”, “CidrIp” : { “Ref” : “SSHLocation”}}
]
}
}
},
“Outputs” : {
“WebsiteURL” : {
“Value” : { “Fn::Join” : [“”, [“http://”, { “Fn::GetAtt” : [ “ElasticLoadBalancer”, “DNSName” ]}]]},
“Description” : “WordPress website”
}
}
}

Below terraform example used to invoke the servers in AWS and to install software using chef.

Important concept/tags in terraform : variables,builders,provisioners

{
"variables": {
"aws_access_key": "",
"aws_secret_key": "",
"run_list": "",
"chef_environment": "",
"role": "",
"region": "",
"ami-base": "",
"ssh-user": ""
},
"builders": [{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",

"region": "{{user `region`}}",
"source_ami": "{{user `ami-base`}}",
"instance_type": "{{user `instance-size`}}",
"ssh_username": "{{user `ssh-user`}}",
"ssh_pty" : true,
"ami_name": "chef-{{user `chef_environment`}}-{{user `role`}}-{{timestamp}}"
}],

"provisioners": [
{
"type": "shell",
"inline": [
"sudo mkdir -p /etc/chef && sudo chown -R root:root /etc/chef",
"sudo mkdir -p /tmp/packer-chef-client && sudo chown -R root:root /tmp/packer-chef-client"
]
},
{
"type": "file",
"source": "{{pwd}}/encrypted_data_bag_secret",
"destination": "/tmp/encrypted_data_bag_secret"
},
{
"type": "file",
"source": "{{pwd}}/validation.pem",
"destination": "/tmp/validator.pem"
},

},
{
"type": "shell",
"inline": [
"sudo mv /tmp/client.rb /etc/chef/client.rb",
"sudo mv /tmp/encrypted_data_bag_secret /etc/chef/encrypted_data_bag_secret",
"sudo mv /tmp/validator.pem /etc/chef/validation.pem && sudo chown -R root:root /etc/chef"
]
},
{
"type": "chef-client",
"execute_command": "cd /etc/chef && sudo chef-client -E {{user `chef_environment`}} -c /etc/chef/client.rb --no-color -j /tmp/packer-chef-client/first-boot.json && sudo cp /etc/chef/client.pem /tmp/packer-chef-client/client.pem",
"chef_environment": "{{user `chef_environment`}}",
"server_url": "https://api.opscode.com/organizations/{{user `org`}}",
"skip_clean_node": "true",
"skip_clean_client": "true",
"run_list": ["{{user `run_list`}}"]
}
]
}

--

--

Venkata Chitturi
DevOps Process and Tools

DevOps Professional. Passionate on learning, implementing and sharing new things.