Thursday, March 23, 2017

cloudformation, cloud-init, cfn-init with redhat 7

I started playing with CloudFormation, Designer a month back and so far it is working out pretty well to all my requirements.

## CloudFormation Designer is pretty nice which allows you can visualize the whole architecture in one frame. It is also very useful and handy if you would like to review just one resource, just select the resource and see code in component tab.
However its kind of painful when the drawing goes haywire and you have to stretch it back to same proportion. Sometime you can cannot select a component directly to drag/move it around. Another problem is with JSON validation. It gives some strange co-ordinates when there is an improper syntax, instead pointing out the line number.

If you are in the same show and looking for a tool to validate JSON,
Please look at http://jsonlint.com/


## It's comparatively very easy to start with CF and deploy a simple infrastructure. For example you can create a VPC, public subnet, private subnet, security groups, Internet gateway, Route table, Network ACLs etc. You can do advance setup like auto scaling, ELB etc also. It may take some time you work with JSON again to figure out EC2 properties like Network interfaces, Network ACL rules,  etc. How depends on works?

Some of the cryptic errors are "Device property cannot be empty". Make sure you review the whole template and see what doesn't make sense. Also the "Events" tab while template is running gives you enough clue what's going on. I don't know if there is any way to try out some of these commands without running the entire template.


## Once you have mastered infrastructure code, then comes application provisioning and deployment. And that's where most of the cloud-init and cfn-init mostly comes into play.
When you can start a EC2 you can do few automation with "UserData", but if you have to do a whole bunch of the things at the time of creation, "UserData" becomes nightmare because the way it reads the syntax.

## cloud-init
Cloud-init will execute whatever is defined in "UserData". For example if you want to get object from private S3 and execute a script or copy a config.
You can execute everything exposing your  AWS Access key and Secret key like we do using CLI in the CF template.
Best practice is to use role for EC2 using Cloudformation Authentication where you only allow the instance to authenticate to the private S3 and download config or execute script using cfn-init.

## cfn-init
For some reason its not very straight forward and documented well if you have to install cfn-init on redhat, as of now atleast.

=====================================================================
"UserData": {
          "Fn::Base64": {
          "Fn::Join": [ "", [ "#!/bin/bash -ex", "\n",
          "cd /root \n",
          "yum -y install unzip wget dos2unix git bind-utils ec2-net-utils \n",
          "curl https://s3.amazonaws.com/aws-cli/awscli-bundle.zip -o awscli-bundle.zip \n",
          "unzip awscli-bundle.zip \n",
          "awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws \n",
           "python awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws \n",
           "curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \n",
           "python get-pip.py \n",
           "pip install pystache \n",
           "pip install argparse \n",
           "pip install python-daemon \n",
           "pip install requests \n",
           "cd /opt \n",
           "curl https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz -o aws-cfn-bootstrap-latest.tar.gz \n",
            "tar -xvpf aws-cfn-bootstrap-latest.tar.gz \n",
            "cd aws-cfn-bootstrap*/ \n",
            "python setup.py build \n",
            "python setup.py install \n",
            "cd /opt \n",
            "mkdir aws \n",
            "cd aws \n",
            "mkdir bin \n",
            "ln -s /usr/bin/cfn-hup /opt/aws/bin/cfn-hup \n",
            "ln -s /usr/bin/cfn-init /opt/aws/bin/cfn-init \n",
            "/opt/aws/bin/cfn-init -v ",
            "         --stack ", { "Ref" : "AWS::StackName" },
            "         --resource upsrcinst1",
            "         --region ", { "Ref" : "AWS::Region" }, "\n",
            "dos2unix /script.sh \n",
            "sh /

script
.sh \n", "\n"
                     ] ]
                 }
             },
                "Tags": [ { "Key": "Name", "Value": "app-server" } ]

              },


Reference:
https://aws.amazon.com/blogs/devops/authenticated-file-downloads-with-cloudformation/

These are the 3 items you have to include in CF script.

1. Create IAM Roles/Policies
Create InstanceRole
Create RolePolicies
Create InstanceProfile



(It will auto create a Role XXXX-InstanceRole-1H2PIS782ZM81)

Under Instance resource:
2. Include in instance metadata:

Create CloudFormation Authentication
Create Cloudformation Init

3. Include IAM Instance profile in instance properties:
"IamInstanceProfile":{ "Ref":"InstanceProfile" },

=====================================================================
"InstanceRole":{
       "Type":"AWS::IAM::Role",
       "Properties":{
             "AssumeRolePolicyDocument":{
              "Statement":[ { "Effect":"Allow", "Principal":{ "Service":[ "ec2.amazonaws.com" ] },  "Action":[  "sts:AssumeRole"  ] } ]
             },
             "Path":"/"
        }
    },


    "RolePolicies":{
         "Type":"AWS::IAM::Policy",
         "Properties":{
              "PolicyName":"S3Download",
              "PolicyDocument":{
                 "Statement":[ {  "Action":[ "s3:GetObject" ], "Effect":"Allow",           "Resource":"arn:aws:s3:::bucket/bucketfolder/*" } ] 
             },
             "Roles":[  { "Ref":"InstanceRole"   } ]
          }
    },


    "InstanceProfile":{
        "Type":"AWS::IAM::InstanceProfile",
        "Properties":{
             "Path":"/",
             "Roles":[  {  "Ref":"InstanceRole"  }  ]
         }
    },


 ====================================================================

"Metadata": {
           "AWS::CloudFormation::Designer": { "id": "85a7776e-769f-4f98-85e0-87e7b58f381f" },

           "AWS::CloudFormation::Authentication":{ "S3AccessCreds":{ "type":"S3", "roleName":{ "Ref":"InstanceRole"  }, "buckets" : ["bucket"]     }  },


              "AWS::CloudFormation::Init": { "config": { "files": { "/root/script.sh": { "source": "https://s3-us-west-2.amazonaws.com/bucket/bucketfolder/script.sh", "mode":"000755", "owner":"ec2-user", "authentication":"S3AccessCreds"  } } } }

        }


=====================================================================
 

"inst1": {
        "Type": "AWS::EC2::Instance",
        "DependsOn": "upsrcebs1",
        "Properties": {
               "IamInstanceProfile":{ "Ref":"InstanceProfile" },

----
----
}

=====================================================================