Skip to content

Instantly share code, notes, and snippets.

@rafaelfelix
Last active October 22, 2015 22:43
Show Gist options
  • Save rafaelfelix/4453f041f5d96ef0ecfd to your computer and use it in GitHub Desktop.
Save rafaelfelix/4453f041f5d96ef0ecfd to your computer and use it in GitHub Desktop.
Deploy 2 NAT instances using the nat_monitor.sh created by Steve Morad (AWS) inside an existing VPC. Based on the original version nat_monitor_demo.template - http://stevemorad.s3.amazonaws.com/reInvent/demos/nat_template.htm
{
"AWSTemplateFormatVersion" : "2010-09-09",
"Description" : "Deploy 2 NAT instances using the nat_monitor.sh created by Steve Morad (AWS) inside an existing VPC. Based on the original version nat_monitor_demo.template - http://stevemorad.s3.amazonaws.com/reInvent/demos/nat_template.htm",
"Parameters" : {
"KeyName" : {
"Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
"Type" : "AWS::EC2::KeyPair::KeyName"
},
"VpcId" : {
"Description" : "VpcId of the VPC where NAT instances will be deployed.",
"Type" : "AWS::EC2::VPC::Id"
},
"ActivePublicSubnetId" : {
"Description" : "SubnetId of the public subnet where the Active NAT instance will be deployed.",
"Type" : "AWS::EC2::Subnet::Id"
},
"StandbyPublicSubnetId" : {
"Description" : "SubnetId of the public subnet where the Standby NAT instance will be deployed (pick one in a different AZ than the Active).",
"Type" : "AWS::EC2::Subnet::Id"
},
"ActiveNATInstanceElasticIPAllocationId" : {
"Description" : "AllocationId of the existing EIP to attach to the Active NAT instance.",
"Type" : "String"
},
"StandbyNATInstanceElasticIPAllocationId" : {
"Description" : "AllocationId of the existing EIP to attach to the Standby NAT instance.",
"Type" : "String"
},
"ActivePrivateRouteTableId" : {
"Description" : "Id of the private route table with 0.0.0.0/0 route going through the Active NAT Instance.",
"Type" : "String"
},
"StandbyPrivateRouteTableId" : {
"Description" : "Id of the private route table with 0.0.0.0/0 route going through the Standby NAT Instance.",
"Type" : "String"
},
"NATSecurityGroupId": {
"Description": "Security group ID to attach to the NAT Nodes.",
"Type": "AWS::EC2::SecurityGroup::Id"
},
"NATNodeInstanceType" : {
"Description" : "Instance type for NAT nodes.",
"Type" : "String",
"Default" : "t2.small",
"AllowedValues" : [ "t2.micro","t2.small","t2.medium","t2.large","m3.medium","m4.xlarge","c4.xlarge","c3.8xlarge"],
"ConstraintDescription" : "must be a valid EC2 instance type."
}
},
"Mappings" : {
"AWSNATAMI" : {
"us-east-1" : { "AMI" : "ami-b0210ed8" },
"us-west-2" : { "AMI" : "ami-75ae8245" },
"us-west-1" : { "AMI" : "ami-ada746e9" },
"eu-west-1" : { "AMI" : "ami-ef76e898" },
"eu-central-1" : { "AMI" : "ami-1e073a03" },
"ap-southeast-1" : { "AMI" : "ami-1a9dac48" },
"ap-southeast-2" : { "AMI" : "ami-43ee9e79" },
"ap-northeast-1" : { "AMI" : "ami-11dc2a11" },
"sa-east-1" : { "AMI" : "ami-63fa417e" }
}
},
"Resources" : {
"NATRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Statement": [ {
"Effect": "Allow",
"Principal": {
"Service": [ "ec2.amazonaws.com" ]
},
"Action": [ "sts:AssumeRole" ]
} ]
},
"Path": "/",
"Policies": [ {
"PolicyName": "NAT_Takeover",
"PolicyDocument": {
"Statement": [ {
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeRouteTables",
"ec2:CreateRoute",
"ec2:ReplaceRoute",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Effect": "Allow",
"Resource": "*"
} ]
}
} ]
}
},
"NATRoleProfile": {
"Type": "AWS::IAM::InstanceProfile",
"Properties": {
"Path": "/",
"Roles": [ {
"Ref": "NATRole"
} ]
}
},
"PrivateRouteActiveNAT" : {
"Type" : "AWS::EC2::Route",
"Properties" : {
"RouteTableId" : { "Ref" : "ActivePrivateRouteTableId" },
"DestinationCidrBlock" : "0.0.0.0/0",
"InstanceId" : { "Ref" : "NAT1Instance" }
}
},
"PrivateRouteStandbyNAT" : {
"Type" : "AWS::EC2::Route",
"Properties" : {
"RouteTableId" : { "Ref" : "StandbyPrivateRouteTableId" },
"DestinationCidrBlock" : "0.0.0.0/0",
"InstanceId" : { "Ref" : "NAT2Instance" }
}
},
"NAT1EIPAssoc" : {
"Type" : "AWS::EC2::EIPAssociation",
"Properties" : {
"InstanceId" : { "Ref" : "NAT1Instance" },
"AllocationId" : { "Ref" : "ActiveNATInstanceElasticIPAllocationId" }
}
},
"NAT2EIPAssoc" : {
"Type" : "AWS::EC2::EIPAssociation",
"Properties" : {
"InstanceId" : { "Ref" : "NAT2Instance" },
"AllocationId" : { "Ref" : "StandbyNATInstanceElasticIPAllocationId" }
}
},
"NAT1Instance" : {
"Type" : "AWS::EC2::Instance",
"Metadata" : {
"Comment1" : "Create NAT #1"
},
"Properties" : {
"InstanceType" : { "Ref" : "NATNodeInstanceType" },
"KeyName" : { "Ref" : "KeyName" },
"IamInstanceProfile" : { "Ref" : "NATRoleProfile" },
"SubnetId" : { "Ref" : "ActivePublicSubnetId" },
"SourceDestCheck" : "false",
"ImageId" : { "Fn::FindInMap" : [ "AWSNATAMI", { "Ref" : "AWS::Region" }, "AMI" ]},
"SecurityGroupIds" : [{ "Ref" : "NATSecurityGroupId" }],
"Tags" : [
{ "Key" : "Name", "Value" : "NAT-Active" }
],
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -v\n",
"set -x\n",
"yum update -y aws*\n",
"yum install -y jq\n",
". /etc/profile.d/aws-apitools-common.sh\n",
"# Download nat_monitor.sh and configure\n",
"cd /root\n",
"wget https://gist.githubusercontent.com/rafaelfelix/5e170b3b732d0b595993/raw/7c2b53f7041f58c628fee9a89cf8a089ca77c451/nat_monitor.sh -O nat_monitor.sh\n",
"# Wait for NAT-Standby to boot up and update StandbyPrivateRouteTableId\n",
"sleep 180\n",
"NAT_ID=\n",
"# CloudFormation should have updated the StandbyPrivateRouteTableId by now (due to yum update), however loop to make sure\n",
"while [ \"$NAT_ID\" == \"\" ]; do\n",
" sleep 60\n",
" NAT_ID=`/usr/bin/aws ec2 describe-route-tables --route-table-ids ",{ "Ref" : "StandbyPrivateRouteTableId" }," --region ", { "Ref" : "AWS::Region" }, " | jq '.RouteTables[]|.Routes[]|select(.DestinationCidrBlock == \"0.0.0.0/0\").InstanceId' | tr -d '\"'`\n",
"done\n",
"# Update NAT_ID, NAT_RT_ID, and My_RT_ID\n",
"sed \"s/NAT_ID=/NAT_ID=$NAT_ID/g\" /root/nat_monitor.sh > /root/nat_monitor.tmp\n",
"sed \"s/NAT_RT_ID=/NAT_RT_ID=",{ "Ref" : "StandbyPrivateRouteTableId" },"/g\" /root/nat_monitor.tmp > /root/nat_monitor.sh\n",
"sed \"s/My_RT_ID=/My_RT_ID=",{ "Ref" : "ActivePrivateRouteTableId" },"/g\" /root/nat_monitor.sh > /root/nat_monitor.tmp\n",
"sed \"s/REGION=/REGION=",{ "Ref" : "AWS::Region" },"/g\" /root/nat_monitor.tmp > /root/nat_monitor.sh\n",
"rm -f /root/nat_monitor.tmp\n",
"chmod a+x /root/nat_monitor.sh\n",
"echo '@reboot /root/nat_monitor.sh > /tmp/nat_monitor.log' | crontab\n",
"/root/nat_monitor.sh > /tmp/nat_monitor.log &\n"
]]}}
}
},
"NAT1InstanceAutoRecoverAlarm": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmDescription": "Trigger a recovery when instance status check fails for 2 consecutive minutes.",
"Namespace": "AWS/EC2" ,
"MetricName": "StatusCheckFailed_System",
"Statistic": "Minimum",
"Period": "60",
"EvaluationPeriods": "2",
"ComparisonOperator": "GreaterThanThreshold",
"Threshold": "0",
"AlarmActions": [ {"Fn::Join" : ["", ["arn:aws:automate:", { "Ref" : "AWS::Region" }, ":ec2:recover" ]]} ],
"Dimensions": [{"Name": "InstanceId","Value": {"Ref": "NAT1Instance"}}]
}
},
"NAT2Instance" : {
"Type" : "AWS::EC2::Instance",
"Metadata" : {
"Comment1" : "Create NAT #2"
},
"Properties" : {
"InstanceType" : { "Ref" : "NATNodeInstanceType" } ,
"KeyName" : { "Ref" : "KeyName" },
"IamInstanceProfile" : { "Ref" : "NATRoleProfile" },
"SubnetId" : { "Ref" : "StandbyPublicSubnetId" },
"SourceDestCheck" : "false",
"ImageId" : { "Fn::FindInMap" : [ "AWSNATAMI", { "Ref" : "AWS::Region" }, "AMI" ]},
"SecurityGroupIds" : [{ "Ref" : "NATSecurityGroupId" }],
"Tags" : [
{ "Key" : "Name", "Value" : "NAT-Standby" }
],
"UserData" : { "Fn::Base64" : { "Fn::Join" : ["", [
"#!/bin/bash -v\n",
"set -x\n",
"yum update -y aws*\n",
"yum install -y jq\n",
"# Download nat_monitor.sh and configure\n",
"cd /root\n",
"wget https://gist.githubusercontent.com/rafaelfelix/5e170b3b732d0b595993/raw/7c2b53f7041f58c628fee9a89cf8a089ca77c451/nat_monitor.sh -O nat_monitor.sh\n",
"# Update NAT_ID, NAT_RT_ID, and My_RT_ID\n",
"sed \"s/NAT_ID=/NAT_ID=",{ "Ref" : "NAT1Instance" },"/g\" /root/nat_monitor.sh > /root/nat_monitor.tmp\n",
"sed \"s/NATT_RT_ID=/NAT_RT_ID=",{ "Ref" : "ActivePrivateRouteTableId" },"/g\" /root/nat_monitor.tmp > /root/nat_monitor.sh\n",
"sed \"s/My_RT_ID=/My_RT_ID=",{ "Ref" : "StandbyPrivateRouteTableId" },"/g\" /root/nat_monitor.sh > /root/nat_monitor.tmp\n",
"sed \"s/REGION=/REGION=",{ "Ref" : "AWS::Region" },"/g\" /root/nat_monitor.tmp > /root/nat_monitor.sh\n",
"rm -f /root/nat_monitor.tmp\n",
"chmod a+x /root/nat_monitor.sh\n",
"echo '@reboot /root/nat_monitor.sh > /tmp/nat_monitor.log' | crontab\n",
"/root/nat_monitor.sh >> /tmp/nat_monitor.log &\n"
]]}}
}
},
"NAT2InstanceAutoRecoverAlarm": {
"Type": "AWS::CloudWatch::Alarm",
"Properties": {
"AlarmDescription": "Trigger a recovery when instance status check fails for 2 consecutive minutes.",
"Namespace": "AWS/EC2" ,
"MetricName": "StatusCheckFailed_System",
"Statistic": "Minimum",
"Period": "60",
"EvaluationPeriods": "2",
"ComparisonOperator": "GreaterThanThreshold",
"Threshold": "0",
"AlarmActions": [ {"Fn::Join" : ["", ["arn:aws:automate:", { "Ref" : "AWS::Region" }, ":ec2:recover" ]]}, { "Ref" : "logical name of an AWS::SNS::Topic resource" } ],
"Dimensions": [{"Name": "InstanceId","Value": {"Ref": "NAT2Instance"}}]
}
},
"NATAllowICMP" : {
"Type" : "AWS::EC2::SecurityGroupIngress",
"Properties" : {
"GroupId" : { "Ref" : "NATSecurityGroupId" },
"IpProtocol" : "icmp",
"FromPort" : "-1",
"ToPort" : "-1",
"SourceSecurityGroupId" : { "Ref" : "NATSecurityGroupId" }
}
}
},
"Outputs" : {
"NAT1" : {
"Description" : "NAT #1.",
"Value" : { "Ref" : "NAT1Instance" }
},
"NAT2" : {
"Description" : "NAT #2.",
"Value" : { "Ref" : "NAT2Instance" }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment