Terraform Module to Build an AMI using CodeBuild and Packer

I’ve created a terraform module that will allow you to build any AMI with a Packer configuration in a Git repository, using CodeBuild, and place the AMI ID in an SSM Parameter for use by other modules.

I’ve created this module so that any AMI that I build can easily be deployed and built on a schedule, with subsequent Lambda function to refresh the ASG with new instances after a new AMI is available.

The module will setup execution of the CodeBuild project on any commit to the Git repo that has the Packer configuration in it, on the main branch. This branch is configured via variable, so it’s easy to control which branch would initiate a new build.

In order for the automated build on PUSH to work, you have to have permission to create webhooks in the Git repository. To do this, be sure to use your own Git repository and setup the Codestar connection to Git, with appropriate permissions.

The resulting CodeBuild project can be triggered manually, via PUSH to var.branch, or via schedule.

The module supports running on a schedule, with the intent to facilitate routine build and deploy models, ie, build a new AMI weekly to get all of the security and patching updates, and then deploy.

The module can be found here.

Note that this module will create a CodeBuild project, SSM Parameter, AMI, and associated IAM requirements. If a schedule is passed into the module, it will also create the CloudWatch Event Target and Event Rule.

A sample terragrunt.hcl to deploy this module with a schedule to build the AMI on Monday at 6:00 AM would look like this:

locals {
  region_vars = yamldecode(file(find_in_parent_folders("region.yaml")))

  module_version = "0.5.0"

  aws_account_id                     = "xxxxxxxxxxx"
  aws_cloudwatch_event_rule_schedule = "0 6 ? * MON *"
  aws_region                         = local.region_vars["aws_region"]
  ami_source                         = "https://github.com/itsaconsulting/nat-gateway-ami"
  subnet_id                          = "subnet-xxxxxxxxxxx"
}

include {
  path = find_in_parent_folders()
}

terraform {
  source = "git::https://github.com/itsaconsulting/terraform-aws-ami?ref=${local.module_version}"
}

generate "provider" {
  path      = "provider.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
provider "aws" {
  region = "${local.aws_region}"
}
EOF
}

generate "backend" {
  path      = "backend.tf"
  if_exists = "overwrite_terragrunt"
  contents  = <<EOF
terraform {
  backend "s3" {}
}
EOF
}

dependency "vpc" {
  config_path = "../vpc"

  mock_outputs_allowed_terraform_commands = ["validate"]
  mock_outputs = { 
    vpc_id = "mock-vpc-id"
  }
}

inputs = { 
  aws_account_id                     = local.aws_account_id
  aws_cloudwatch_event_rule_schedule = local.aws_cloudwatch_event_rule_schedule
  aws_region                         = local.aws_region
  ami_source                         = local.ami_source
  subnet_id                          = local.subnet_id
  vpc_id                             = dependency.vpc.outputs.vpc_id
}

Note that the default Packer Git repository is a repository that I have created to build a NAT Gateway equivalent AMI, so you can test with that and later customize with something of your own.

Also, pass in the desired SSM Parameter name to customize where the AMI ID is stored. This will allow you to configure your ASG to use this parameter on deploy, and then user a Lambda or other mechanism to perform an Instance Refresh on the ASG.

To use the SSM Parameter value as an input to your AutoScaling Group, use something like the following:

locals {
  ami_image_id = data.aws_ssm_parameter.ami_image_id.value
  ...
}
...
data "aws_ssm_parameter" "ami_image_id" {
  name = var.ssm_parameter_ami_image_id
}
...
resource "aws_launch_template" "test_launch_template" {
  name_prefix   = "test-launch-template-"
  image_id      = local.ami_image_id
  instance_type = local.instance_type
  iam_instance_profile {
    arn = aws_iam_instance_profile.test_instance_profile.arn
  }

  vpc_security_group_ids = [aws_security_group.test_sg.id]
}

I am working on a series that will allow anyone to easily replace the NAT Gateway with an AutoScaled EC2 instance per AZ, so keep in touch to follow along.

Need help implementing a solution like this? Get in touch using the contact form on this site and I’d be happy to help.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *