Monday 4 March 2024

HashiCorp Packer



Packer is a tool for creating (golden) images from a configurable template. 

Packer configuration file is a JSON file e.g. config.json.

config.json contains:
  • variables
  • builders
    • type
      •  amazon-ebs
  • provisioners
    • type
      • shell
      • ansible

Example of configuration file used to create AWS AMI for multiple environments, by using Ansible as a provisioner:

config.json:

{
  "variables": {
    "env_name": "{{env `env_name`}}",
    "instance_profile": "",
    "ami_name": "asb-linux-al23-arm-{{timestamp}}",
    "kms_key_id": "{{env `kms_key_id`}}",
    "sg_default": "{{env `SG_DEFAULT`}}",
    "current_user": "{{env `USER`}}"
  },
  "builders": [
    {
      "ami_name": "{{user `ami_name`}}",
      "instance_type": "t4g.medium",
      "region": "us-east-1",
      "source_ami_filter": {
        "filters": {
          "virtualization-type": "hvm",
          "name": "al2023-ami-2023.*-kernel-6.1-arm64",
          "root-device-type": "ebs"
        },
        "owners": "amazon",
        "most_recent": true
      },
      "ssh_username": "ec2-user",
      "ssh_bastion_host": "bastion.mycorp.com",
      "ssh_bastion_username": "{{user `current_user`}}",
      "ssh_bastion_agent_auth": true,
      "ssh_bastion_port": 22,
      "ssh_timeout": "2m",
      "ssh_clear_authorized_keys": "true",
      "iam_instance_profile": "{{user `instance_profile`}}",
      "type": "amazon-ebs",
      "tags": {
        "Name": "{{user `ami_name`}}",
        "Environment": "{{user `env_name`}}"
      },
      "vpc_filter": {
        "filters": {
          "tag:Environment": "{{user `env_name`}}",
          "isDefault": "false"
        }
      },
      "subnet_filter": {
        "filters": {
          "tag:Name": "vpc-{{user `env_name`}}-private-us-east-*"
        },
        "most_free": true
      },
      "security_group_ids": [
        "{{user `sg_default`}}"
      ],
      "launch_block_device_mappings": [
        {
          "device_name": "/dev/xvda",
          "encrypted": true,
          "kms_key_id": "{{user `kms_key_id`}}",
          "delete_on_termination": true,
          "volume_type": "gp3"
        }
      ]
    }
  ],
  "provisioners": [
    {
      "type": "shell",
      "inline": [
        "sudo dnf update -y",
        "sudo dnf install -y python3"
      ]
    },
    {
      "type": "ansible",
      "host_alias": "packer",
      "user": "ec2-user",
      "inventory_directory": "ansible/env/aws/{{user `env_name`}}",
      "playbook_file": "ansible/playbooks/playbook.yml",
      "extra_arguments": [
        "-D",
        "--vault-password-file",
        "./aws-{{user `env_name`}}-vault.pw",
        "--scp-extra-args", "'-O'",
        "--extra-vars",
        "'ansible_python_interpreter=/usr/bin/python3'"
      ]
    }
  ]
}


To run Packer:
 
$ packer build [-debug] config.json


AWS AMI gets created in the following way:
  • a temporary SSH keypair is created
  • new temporary EC2 instance is started. This instance is based on source AMI specified in configuration file
  • SSH tunnel is established between local/dev host and remote EC2 instance
  • provisioners are run e.g. Ansible is installing packages etc...
  • once provisioners work is completed ephemeral key is removed from authorized_keys file on temp EC2 instance
  • temp EC2 instance is terminated
  • a snapshot of the root disk of that EC2 instance is created
  • a new AMI is created based on that snapshot
  • tags are added to snapshot
  • tags are added to AMI
  • temp SSH keypair is destroyed

Once AMI is created, it is not possible to find out which AMI was used as a source (base) AMI (amazon web services - Is it possible to find the source AMI for an existing AMI? - Stack Overflow). But soon after AMI is created, we can check properties of temp EC2 instance (which is in Terminated state) and check its AMI. For source AMI filter as above, it could be e.g. al2023-ami-2023.3.20240219.0-kernel-6.1-arm64.

---

No comments: