/infrastructure-immersion-day

Custom Immersion Day to show how infrastructure and operations can use modern CI/CD practices on AWS

Overview

The purpose of this lab is to expose infrastructure and operations engineers to infrastructure as code. We use CodePipeline to practice continuous integration/ continuous deployment of our code. We create a single instance, then auto scale it in response to load. We the use the auto scale group to facilitate a rolling refresh of our operating system.

Things to keep in mind:

  • This lab supports the following regions:
    • us-east-1
    • us-east-1
    • us-west-2
  • Additional regions can be supported if the AMIs are provided in the AMI map
  • Case matters, especially when it comes to stack names

todo:

  • Create architecture diagrams for each phase
  • Add SMS notification for auto scaling events
  • Add out of the box support for other regions

Prereq

Install the following software on your laptop/desktop:

This section is only completed once and likely already has been done for you.

  • We need a few roles for CodePipeline

    Windows:

    aws cloudformation create-stack ^
      --stack-name iac-prereq-access ^
      --template-body file://prereq-access.yml ^
      --capabilities CAPABILITY_NAMED_IAM
    

    Mac/Linux:

    aws cloudformation create-stack \
      --stack-name iac-prereq-access \
      --template-body file://prereq-access.yml \
      --capabilities CAPABILITY_NAMED_IAM
    
  • We also need a VPC and subnet

    Windows:

    aws cloudformation create-stack ^
      --stack-name iac-prereq-network ^
      --template-body file://prereq-network.yml ^
      --parameters file://prereq-parameters.json
    

    Mac/Linux:

    aws cloudformation create-stack \
      --stack-name iac-prereq-network \
      --template-body file://prereq-network.yml \
      --parameters file://prereq-parameters.json
    
  • Users creating a CodePipeline will need the ability to pass an IAM role. Here is the policy needed:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "IaCPassRole",
                "Effect": "Allow",
                "Action": "iam:PassRole",
                "Resource": "*"
            }
        ]
    }

Module 0 - CI/CD pipeline

We're going to create CodeCommit and CodePipeline resources to help us execute new CloudFormation templates after committing them to a source code repository

Steps:

  1. Open 0-parameters.json and provide a value for YourName and PipelineRoleArn.
  2. Save this file.
  3. Run the following command to set up CodeCommit and CodePipeline:

Windows:

aws cloudformation create-stack ^
  --stack-name <your_cicd_stack_name> ^
  --template-body file://0-ci-cd.yml ^
  --parameters file://0-parameters.json

Mac/Linux:

aws cloudformation create-stack \
  --stack-name <your_cicd_stack_name> \
  --template-body file://0-ci-cd.yml \
  --parameters file://0-parameters.json
  1. The CodeCommit and CodePipeline resources should be created in less than 30 seconds. Run the following command to check the status. Or check the console.
aws cloudformation describe-stacks --stack-name <your_cicd_stack_name>
  1. When the pipeline is created you can grab just the repository URL and clone it: Windows:
aws cloudformation describe-stacks ^
  --stack-name <your_cicd_stack_name> ^
  --query "Stacks[0].Outputs[0].OutputValue" ^
  --output text
git clone <repo_url>

Mac/Linux:

aws cloudformation describe-stacks \
  --stack-name <your_cicd_stack_name> \
  --query 'Stacks[0].Outputs[0].OutputValue' \
  --output text | xargs -I {} \
      git clone {}
  1. Stop here and take a moment to review the resources you have just created.

Review:

Module 1 - Deploy infrastructure as code

Review the CloudFormation template, 1-instance.yml, for this module and try to answer the questions below before getting started.

Questions:

  • What are we creating?
  • Where are the resources being created? How do we control this?
  • What will the resources do when they start?
  • What does CodePipeline expect our files to be named?
  • Will these instructions cause any resources to be destroyed/recreated?

Steps:

  1. Open 1-parameters.json and provide a value for YourName and SubnetId.
  2. Save this file.
  3. Deploy infrastructure as code through the CI/CD pipeline:

Windows:

copy 1-instance.yml <your_repo_name>\template.yml
copy 1-parameters.json <your_repo_name>\parameters.json
cd <your_repo_name>
git add -A
git commit -m "Creating an instance"
git push
cd ..

Mac/Linux:

cp 1-instance.yml <your_repo_name>/template.yml
cp 1-parameters.json <your_repo_name>/parameters.json
cd <your_repo_name>
git add -A
git commit -m "Creating an instance"
git push
cd ..
  1. Monitor the pipeline through the console.
  2. Once the 'Deploy' stage is complete review the following:
  • Your Code Pipeline has created a CloudFormation stack with your name
  • EC2 instance is created (Console > EC2 > Instances)
  • CPU has spiked to ~100% (Select instance > Monitoring tab > CPU Utilization)
  • Don't complete the 'Approve' stage yet because doing so will delete your infrastructure in the next step
  1. Adjust the size of the instance and create a new commit.
  • Set the size to t2.small
  1. Verify your change in the console.
  2. Change the user data to run less frequently and create a new commit.
  • Set the cron to */2 * * * *
  1. Verify your change in the console.

Module 2 - Autoscale

Review the CloudFormation template, 2-auto-scale.yml, for this module and try to answer the questions below before getting started.

Questions:

  • What resources are being added?
  • How will our resources scale? How can we control this?

Steps:

  1. Deploy autoscale resources through your CI/CD pipeline:

Windows:

copy 2-auto-scale.yml <your_repo_name>\template.yml
cd <your_repo_name>
git add -A
git commit -m "Creating instance with auto scale capabilities"
git push
cd ..

Mac/Linux:

cp 2-auto-scale.yml <your_repo_name>/template.yml
cd <your_repo_name>
git add -A
git commit -m "Creating instance with auto scale capabilities"
git push
cd ..
  1. Adjust the amount of time between scaling events and create a new commit.
  • Set the new value to 120 seconds
  1. Verify your change in the console.
  2. Adjust the max number of instances and create a new commit.
  • Set the new value to 3
  1. Verify your change in the console.

Review:

Module 3 - Patch / Refresh

Review the CloudFormation template, 3-refresh.yml, for this module and try to answer the questions below before getting started.

Questions:

  • How could we apply patches to an instance?

Steps:

  1. Complete the 'Approve' step in your pipeline.
  2. Confirm the 'Delete' step completes.
  3. Open 3-parameters.json and provide a value for YourName, SubnetId, and OS (use "AmazonLinux2" as the OS value)
  4. Save this file.
  5. Deploy resources through your CI/CD pipeline:

Windows:

copy 3-refresh.yml <your_repo_name>\template.yml
copy 3-parameters.json <your_repo_name>\parameters.json
cd <your_repo_name>
git add -A
git commit -m "Creating instances that need to be refreshed"
git push
cd ..

Mac/Linux:

cp 3-refresh.yml <your_repo_name>/template.yml
cp 3-parameters.json <your_repo_name>/parameters.json
cd <your_repo_name>
git add -A
git commit -m "Creating instances that need to be refreshed"
git push
cd ..
  1. Change the OS value to RedHat7 in your parameters file and create a new commit.
  2. Watch what happens to your instances in the console.

Review:

Module 4 - Disaster Recovery

Review the CloudFormation template, 4-dr.yml, for this module and try to answer the questions below before getting started.

Questions:

  • What region and availability zone will the resources be created in?
  • Where is the AMI magic happening? How would you expand this?

Steps:

  1. Change your profile to another region
aws configure set region us-west-2
  1. Open 4-parameters.json and provide a value for YourName and OS (use "AmazonLinux2" or "RedHat7" as the OS value)
  2. Deploy CloudFormation

Windows:

aws cloudformation create-stack ^
  --stack-name <your_dr_stack_name> ^
  --template-body file://4-dr.yml ^
  --parameters file://4-parameters.json

Mac/Linux:

aws cloudformation create-stack \
  --stack-name <your_dr_stack_name> \
  --template-body file://4-dr.yml \
  --parameters file://4-parameters.json
  1. Add another availability zone and create a new commit.

Review:

Clean up

  1. Go to CodePipeline and complete the 'Approve' stage. Confirm the 'Delete' stage completes.
  2. Tear down the DR stack:
aws cloudformation delete-stack --stack-name <your_dr_stack_name>
  1. Change your profile back to the original region
aws configure set region us-east-1
  1. Delete the contents of the CI/CD artifact bucket:

Windows:
(Or use the console because I'm not sure how to write this as a one-liner)

aws cloudformation describe-stack-resources ^
  --stack-name <your_cicd_stack_name> ^
  --query "StackResources[?LogicalResourceId==`Artifacts`].PhysicalResourceId" ^
  --output text
aws s3 rm s3://<output_from_line_above> --recursive

Mac/Linux:

aws cloudformation describe-stack-resources \
  --stack-name <your_cicd_stack_name> \
  --query 'StackResources[?LogicalResourceId==`Artifacts`].PhysicalResourceId' \
  --output text | xargs -I {} \
      aws s3 rm s3://{} --recursive
  1. Tear down the CI/CD stack:
aws cloudformation delete-stack --stack-name <your_cicd_stack_name>
  1. (Optional) If you created the prereq resources, tear those down:
aws cloudformation delete-stack --stack-name iac-prereq-network
aws cloudformation delete-stack --stack-name iac-prereq-access

Miscellaneous

Required software:

  • AWS CLI
  • jq library

List Amazon Linux 2 AMIs in a region:

aws ec2 describe-images \
  --filters "Name=owner-alias,Values=amazon" \
    "Name=name,Values=amzn2-ami-hvm-*-gp2" \
    "Name=architecture,Values=x86_64" \
  --owner "amazon" \
  --region us-east-1 | \
  jq '.Images | sort_by(.CreationDate) | .[] | {Name: .Name, Created: .CreationDate, Id: .ImageId}' \
  > amazonlinux-east-2

List Red Hat AMIs in a region:

aws ec2 describe-images \
  --filters "Name=name,Values=RHEL*" \
    "Name=architecture,Values=x86_64" \
  --region us-east-1 | \
  jq '.Images | sort_by(.CreationDate) | .[] | {Name: .Name, Created: .CreationDate, Id: .ImageId}' \
  > rhel-east-2