DEV Community

nainarmalik
nainarmalik

Posted on

Building and Sharing an AMI Using AWS Image Builder

AWS Image Builder is a powerful tool that helps automate the process of creating and distributing Amazon Machine Images (AMIs) across AWS accounts or regions. In many scenarios, you might want to share an AMI with another AWS account without actually copying it to that account, reducing storage costs and making management easier.

In this blog post, I will walk you through how I set up an Image Builder pipeline to create a custom Windows Server 2022 AMI. As part of this process, we will install Terraform, the AWS PowerShell Module, and other tools. The key objective is to share the AMI with a target AWS account without copying it. This setup is useful for environments where you need to distribute AMIs for usage across different accounts while keeping centralized control over the image versions.

By the end of this guide, you’ll understand how to:

Set up the necessary AWS infrastructure (VPC, Subnets, IAM roles) for the Image Builder process.
Create Image Builder components to install tools like Terraform and AWS PowerShell on a Windows Server 2022 base image.
Build and share the AMI with a target AWS account without copying it, saving storage and simplifying management.
Step 1: Setting Up the AWS Infrastructure
Before creating and distributing an AMI, it’s important to set up the networking and security infrastructure to host the EC2 instance that Image Builder will use to create the AMI.

1.1 Create a Custom VPC
We start by setting up a custom Virtual Private Cloud (VPC) to provide the networking environment for our resources. This VPC includes a public subnet, an Internet Gateway for internet access, and a route table to direct traffic.

resource "aws_vpc" "custom_vpc" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = true
  tags = { Name = "CustomVPC" }
}
Enter fullscreen mode Exit fullscreen mode

1.2 Create a Public Subnet and Internet Gateway
Next, we create a public subnet within the VPC. This subnet will allow the EC2 instances to have public IP addresses for accessing resources like AWS services or the internet. An Internet Gateway and appropriate route table are also created to route traffic.

resource "aws_subnet" "public_subnet" {
  vpc_id                  = aws_vpc.custom_vpc.id
  cidr_block              = "10.0.1.0/24"
  map_public_ip_on_launch = true
  tags = { Name = "PublicSubnet" }
}

resource "aws_internet_gateway" "custom_igw" {
  vpc_id = aws_vpc.custom_vpc.id
  tags = { Name = "CustomInternetGateway" }
}

resource "aws_route_table" "custom_route_table" {
  vpc_id = aws_vpc.custom_vpc.id
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.custom_igw.id
  }
  tags = { Name = "CustomRouteTable" }
}
Enter fullscreen mode Exit fullscreen mode

1.3 Create a Security Group
A security group is created to allow inbound and outbound traffic to/from the EC2 instances in the public subnet. Here, all traffic is allowed, but in production, you might want to restrict access to specific ports.

resource "aws_security_group" "custom_sg" {
  vpc_id      = aws_vpc.custom_vpc.id
  description = "Security group for allowing all inbound and outbound traffic"
  ingress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"  # All protocols
    self        = true  # Allow traffic from itself
  }
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
  tags = { Name = "CustomSecurityGroup" }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Setting Up the EC2 Instance Profile and IAM Role
To allow the EC2 instances to interact with other AWS services, such as SSM (AWS Systems Manager) and Image Builder, we need an IAM role with appropriate permissions. This role will be associated with an EC2 instance profile.

resource "aws_iam_role" "custom_ec2_instance_profile_role" {
  name = "EC2InstanceProfileForImageBuilderCustom"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
        Effect    = "Allow",
        Principal = { Service = "ec2.amazonaws.com" },
        Action    = "sts:AssumeRole"
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "custom_ec2_ssm_policy" {
  role       = aws_iam_role.custom_ec2_instance_profile_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
Enter fullscreen mode Exit fullscreen mode

This IAM role allows the EC2 instance to interact with the AWS Systems Manager (SSM) for seamless management of the instance during AMI creation.

Step 3: Creating AWS Image Builder Components
AWS Image Builder allows us to define custom components (scripts and instructions) that will be executed during the AMI creation process. Let’s create two custom components that install Terraform and the AWS PowerShell module.

3.1 Component to Install Terraform
We can create a component that uses PowerShell to install Terraform during the AMI build process.

resource "aws_imagebuilder_component" "terraform_powershell_component" {
  name        = "TerraformThroughPowershell"
  version     = "1.0.0"
  platform    = "Windows"
  data = <<EOT
{
  "schemaVersion": "1.0",
  "phases": [
    {
      "name": "build",
      "steps": [
        {
          "name": "InstallTerraform",
          "action": "ExecutePowerShell",
          "inputs": {
            "commands": [
              "Invoke-WebRequest -Uri 'https://releases.hashicorp.com/terraform/1.5.0/terraform_1.5.0_windows_amd64.zip' -OutFile 'C:\\terraform.zip';",
              "Expand-Archive -Path 'C:\\terraform.zip' -DestinationPath 'C:\\terraform';",
              "Move-Item -Path 'C:\\terraform\\terraform.exe' -Destination 'C:\\Windows\\System32\\';",
              "terraform -version;"
            ]
          }
        }
      ]
    }
  ]
}
EOT
}
Enter fullscreen mode Exit fullscreen mode

3.2 Component to Install NuGet and AWS PowerShell
This component installs NuGet and the AWS PowerShell module, allowing the EC2 instance to interact with AWS services.

resource "aws_imagebuilder_component" "nuget_aws_powershell_component" {
  name        = "NuGetAndAWSPowerShellInstaller"
  version     = "1.0.0"
  platform    = "Windows"
  data = <<EOT
{
  "schemaVersion": "1.0",
  "phases": [
    {
      "name": "build",
      "steps": [
        {
          "name": "InstallNuGetAndAWSPowerShell",
          "action": "ExecutePowerShell",
          "inputs": {
            "commands": [
              "Install-PackageProvider -Name NuGet -Force -Scope CurrentUser;",
              "Install-Module -Name AWSPowerShell -AllowClobber -Force -SkipPublisherCheck;"
            ]
          }
        }
      ]
    }
  ]
}
EOT
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Creating an AWS Image Builder Recipe
An Image Builder recipe combines components and parent images to define how an AMI should be built. In this case, we are using Windows Server 2022 as the base image and adding the Terraform and AWS PowerShell components to it.

resource "aws_imagebuilder_image_recipe" "custom_recipe" {
  name          = "CustomRecipe"
  version       = "1.0.0"
  parent_image  = "arn:aws:imagebuilder:eu-central-1:aws:image/windows-server-2022-english-core-base-x86/x.x.x"

  component {
    component_arn = aws_imagebuilder_component.terraform_powershell_component.arn
  }

  component {
    component_arn = aws_imagebuilder_component.nuget_aws_powershell_component.arn
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Sharing the AMI Without Copying It
AWS Image Builder can share the AMI with a target account by setting launch_permission in the distribution configuration. This allows the target account to launch instances using the AMI without copying it.

resource "aws_imagebuilder_distribution_configuration" "custom_distribution" {
  name = "CustomDistribution"

  distribution {
    region = "eu-central-1"

    ami_distribution_configuration {
      name = "${var.ami_name}-{{ imagebuilder:buildDate }}"

      launch_permission {
        user_ids = [var.target_account_id]
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Creating the Image Builder Pipeline
Finally, the Image Builder pipeline is configured to orchestrate the entire AMI build process.

resource "aws_imagebuilder_image_pipeline" "custom_pipeline" {
  name                               = "CustomPipeline"
  image_recipe_arn                   = aws_imagebuilder_image_recipe.custom_recipe.arn
  infrastructure_configuration_arn   = aws_imagebuilder_infrastructure_configuration.custom_infra_config.arn
  distribution_configuration_arn     = aws_imagebuilder_distribution_configuration.custom_distribution.arn
  status                             = "ENABLED"
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Using AWS Image Builder, we can easily automate the creation and sharing of AMIs across accounts. In this example, we set up a Windows Server 2022 AMI and installed useful tools like Terraform and the AWS PowerShell module. By configuring launch permissions, the AMI can be shared with other AWS accounts without copying it, reducing storage costs and simplifying management. This setup, combined with Terraform, makes the process repeatable and efficient.

Top comments (0)