Packer’s IAM profile for building images needs just enough access to create resources, and no more, to prevent your build infrastructure from becoming a launchpad for unauthorized actions.

Let’s see a Packer build in action, specifically how it assumes an IAM role to get permissions.

Imagine you’re building an AMI for EC2 instances. Packer needs to:

  1. Create a temporary EC2 instance: This instance will run the build process.
  2. Attach a temporary EBS volume: This volume will be used as the root device for the instance.
  3. Create a snapshot of the EBS volume: This is the core of creating the AMI.
  4. Create the AMI from the snapshot: This finalizes the image.
  5. Clean up: Terminate the instance and delete the EBS volume and snapshot.

To do this, Packer needs an IAM role attached to the EC2 instance that it launches. This role is often called an "instance profile" in AWS.

Here’s a simplified amazon-ebs.json Packer template showing the iam_instance_profile setting:

{
  "variables": {

    "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}",


    "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}",

    "region": "us-east-1"
  },
  "builders": [
    {
      "type": "amazon-ebs",

      "access_key": "{{user `aws_access_key`}}",


      "secret_key": "{{user `aws_secret_key`}}",


      "region": "{{user `region`}}",

      "source_ami_filter": {
        "filters": {
          "name": "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-20230919",
          "virtualization-type": "hvm",
          "root-device-type": "ebs"
        },
        "owners": ["099720109477"]
      },
      "instance_type": "t3.micro",
      "ssh_username": "ubuntu",

      "ami_name": "my-ubuntu-ami-{{timestamp}}",

      "iam_instance_profile": "packer-build-role"
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "sudo apt-get update",
        "sudo apt-get install -y nginx",
        "echo 'Hello from Packer' | sudo tee /var/www/html/index.html"
      ]
    }
  ]
}

Notice the iam_instance_profile: "packer-build-role" line. This tells Packer to associate an IAM instance profile named packer-build-role with the EC2 instance it launches for the build. The EC2 instance will then assume the IAM role defined by this instance profile.

The core problem this solves is credential management. Instead of embedding long-lived AWS access keys directly into your Packer configuration or CI/CD system, you grant temporary, role-based access to the build environment itself. The EC2 instance running Packer assumes a role, and that role has the necessary permissions.

Here’s how the packer-build-role would be configured on the AWS side (this is not Packer config, but the IAM role Packer uses):

IAM Role (packer-build-role):

  • Trust Relationship: This role must be trusted by ec2.amazonaws.com. This means the EC2 service can assume this role.

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Principal": {
            "Service": "ec2.amazonaws.com"
          },
          "Action": "sts:AssumeRole"
        }
      ]
    }
    
  • Permissions Policy: This is where we define the least privilege.

    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "ec2:CreateSnapshot",
            "ec2:CreateTags",
            "ec2:DescribeAvailabilityZones",
            "ec2:DescribeImages",
            "ec2:DescribeInstances",
            "ec2:DescribeSnapshots",
            "ec2:DescribeSubnets",
            "ec2:DescribeVolumes",
            "ec2:DescribeVpcs",
            "ec2:ModifyImageAttribute",
            "ec2:RegisterImage",
            "ec2:RunInstances",
            "ec2:TerminateInstances",
            "ec2:CreateVolume",
            "ec2:AttachVolume",
            "ec2:DetachVolume",
            "ec2:DeleteVolume",
            "ec2:DeleteSnapshot",
            "ec2:DeleteTags",
            "iam:PassRole"
          ],
          "Resource": "*"
        },
        {
          "Effect": "Allow",
          "Action": "iam:PassRole",
          "Resource": "arn:aws:iam::YOUR_ACCOUNT_ID:role/packer-build-role",
          "Condition": {
            "StringEquals": {
              "iam:PassedToService": "ec2.amazonaws.com"
            }
          }
        }
      ]
    }
    

The iam:PassRole permission is crucial here. It allows the EC2 service to pass the packer-build-role to the EC2 instance when it’s launched. Without this, the instance would launch but wouldn’t be able to assume the role, and Packer would fail. The condition iam:PassedToService: "ec2.amazonaws.com" ensures that this role can only be passed to EC2 instances, not other services.

This granular policy is the "least privilege" part. We’re only giving permissions for the specific EC2 actions needed to create an AMI. We’re not granting iam:CreateUser, s3:DeleteBucket, or kms:Decrypt which would be excessive and dangerous.

The iam:CreateTags permission is often overlooked but important for good practice. Packer uses tags to identify resources it creates (like temporary EBS volumes or EC2 instances) so it can clean them up later. Without CreateTags, it might fail to clean up properly.

The ec2:Describe* permissions are for observation. Packer needs to be able to query AWS for information about subnets, VPCs, existing AMIs, etc., to make informed decisions during the build process.

When Packer starts, it launches an EC2 instance. This instance, upon booting, automatically receives temporary security credentials associated with the packer-build-role. Packer (running on that EC2 instance) then uses these credentials to make API calls to AWS to perform the AMI creation steps. Once the AMI is built, Packer instructs the instance to terminate itself, and the associated role’s permissions are used to delete the temporary resources.

If you were running Packer outside of an EC2 instance (e.g., on your laptop or in a CI/CD runner), you would typically use AWS access keys and secret keys configured in Packer’s variables or environment. In that scenario, the IAM user or role associated with those keys would need the iam:PassRole permission to pass the packer-build-role to the EC2 instance it launches. The distinction is where the credentials originate: from the EC2 instance’s instance profile, or from explicit access keys provided to Packer.

The next logical step after mastering least-privilege IAM for builds is understanding how to manage secrets within your provisioners, separate from the build infrastructure’s IAM permissions.

Want structured learning?

Take the full Packer course →