Packer
Example of an AMI build with Packer and provisioned with Ansible
The name of your Packer configuration file must be <file_name>.pkr.hcl or <file_name>.pkr.json.
packer { required_plugins { amazon = { version = ">= 1.2.8" source = "github.com/hashicorp/amazon" } ansible = { version = "~> 1" source = "github.com/hashicorp/ansible" } }}
source "amazon-ebs" "ubuntu" { ami_name = "ansible-ami" instance_type = "t2.micro" region = "us-east-2" ssh_username = "ubuntu" source_ami_filter { filters = { name = "ubuntu/images/hvm-ssd/ubuntu-focal-20.04-amd64-server-*" root-device-type = "ebs" virtualization-type = "hvm" } most_recent = true owners = ["099720109477"] }}
build { name = "test-packer" sources = [ "source.amazon-ebs.ubuntu" ]
provisioner "ansible" { user = "ubuntu" playbook_file = "playbook.yml" extra_arguments = ["--scp-extra-args", "'-O'"] }}Export your AWS credentials
$ export access_key = "<aws_access_key>"$ export secret_key = "<aws_secret_key>"Create your Ansible playbook to provision the AMI
---- name: "Apache configuration" hosts: all remote_user: ubuntu
tasks: - name: Ensure Apache is at the latest version apt: name: apache2 state: latest update_cache: yes become: true
- name: enabled mod_rewrite become: true apache2_module: name=rewrite state=present
- name: apache2 listen on port 443 become: true lineinfile: dest=/etc/apache2/ports.conf regexp="^Listen 80" line="Listen 443" state=present
- name: apache2 virtualhost on port 443 become: true lineinfile: dest=/etc/apache2/sites-available/000-default.conf regexp="^<VirtualHost \*:80>" line="<VirtualHost *:443>" state=present
- name: Remove default /var/www/html/ become: true file: path: /var/www/html state: absent
- name: Git checkout website become: true git: repo: https://github.com/cloudacademy/static-website-example dest: /var/www/html notify: - start apache2
handlers: - name: start apache2 service: name=apache2 state=startedRun Packer
Check your Packer version
$ packer version> Packer v1.7.2Initialize your Packer configuration
This command will install the required plugins.
$ packer init .Format the Packer configuration
$ packer fmt config.pkr.hcl> config.pkr.hclValidate your Packer configuration
$ packer validate config.pkr.hcl> The configuration is valid.Build your AMI
$ packer build config.pkr.hcl
test-packer.amazon-ebs.ubuntu: output will be in this color.
==> test-packer.amazon-ebs.ubuntu: Prevalidating any provided VPC information==> test-packer.amazon-ebs.ubuntu: Prevalidating AMI Name: ansible-ami test-packer.amazon-ebs.ubuntu: Found Image ID: ami-07e4e8101a24d7fb5==> test-packer.amazon-ebs.ubuntu: Creating temporary keypair: packer_65b91c67-9340-c65b-6326-50779d27cd81==> test-packer.amazon-ebs.ubuntu: Creating temporary security group for this instance: packer_65b91c69-bfaa-0870-164b-017b74dc3ee7==> test-packer.amazon-ebs.ubuntu: Authorizing access to port 22 from [0.0.0.0/0] in the temporary security groups...==> test-packer.amazon-ebs.ubuntu: Launching a source AWS instance... test-packer.amazon-ebs.ubuntu: Instance ID: i-0f720d4a051b98d80==> test-packer.amazon-ebs.ubuntu: Waiting for instance (i-0f720d4a051b98d80) to become ready...==> test-packer.amazon-ebs.ubuntu: Using SSH communicator to connect: 18.118.130.55==> test-packer.amazon-ebs.ubuntu: Waiting for SSH to become available...==> test-packer.amazon-ebs.ubuntu: Connected to SSH!==> test-packer.amazon-ebs.ubuntu: Provisioning with Ansible... test-packer.amazon-ebs.ubuntu: Setting up proxy adapter for Ansible....==> test-packer.amazon-ebs.ubuntu: Executing Ansible: ansible-playbook -e packer_build_name="ubuntu" -e packer_builder_type=amazon-ebs --ssh-extra-args '-o IdentitiesOnly=yes' --scp-extra-args '-O' -e ansible_ssh_private_key_file=/var/folders/rp/hnntscqx44n4gc658zqbwg1r0000gn/T/ansible-key249877241 -i /var/folders/rp/hnntscqx44n4gc658zqbwg1r0000gn/T/packer-provisioner-ansible2320871080 /Users/badr/Documents/Personnal_Project/Portfolio/packer_test/ansible/playbook.yml test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: PLAY [Apache configuration] **************************************************** test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [Gathering Facts] ********************************************************* test-packer.amazon-ebs.ubuntu: ok: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [Ensure Apache is at the latest version] ********************************** test-packer.amazon-ebs.ubuntu: changed: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [enabled mod_rewrite] ***************************************************** test-packer.amazon-ebs.ubuntu: changed: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [apache2 listen on port 443] ********************************************** test-packer.amazon-ebs.ubuntu: changed: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [apache2 virtualhost on port 443] ***************************************** test-packer.amazon-ebs.ubuntu: changed: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [Remove default /var/www/html/] ******************************************* test-packer.amazon-ebs.ubuntu: changed: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: TASK [Git checkout website] **************************************************** test-packer.amazon-ebs.ubuntu: changed: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: RUNNING HANDLER [started apache2] ********************************************** test-packer.amazon-ebs.ubuntu: ok: [default] test-packer.amazon-ebs.ubuntu: test-packer.amazon-ebs.ubuntu: PLAY RECAP ********************************************************************* test-packer.amazon-ebs.ubuntu: default : ok=8 changed=6 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 test-packer.amazon-ebs.ubuntu:==> test-packer.amazon-ebs.ubuntu: Stopping the source instance... test-packer.amazon-ebs.ubuntu: Stopping instance==> test-packer.amazon-ebs.ubuntu: Waiting for the instance to stop...==> test-packer.amazon-ebs.ubuntu: Creating AMI ansible-ami from instance i-0f720d4a051b98d80 test-packer.amazon-ebs.ubuntu: AMI: ami-0daea26f520e2cfce==> test-packer.amazon-ebs.ubuntu: Waiting for AMI to become ready...==> test-packer.amazon-ebs.ubuntu: Skipping Enable AMI deprecation...==> test-packer.amazon-ebs.ubuntu: Terminating the source AWS instance...==> test-packer.amazon-ebs.ubuntu: Cleaning up any extra volumes...==> test-packer.amazon-ebs.ubuntu: No volumes to clean up, skipping==> test-packer.amazon-ebs.ubuntu: Deleting temporary security group...==> test-packer.amazon-ebs.ubuntu: Deleting temporary keypair...Build 'test-packer.amazon-ebs.ubuntu' finished after 5 minutes 27 seconds.
==> Wait completed after 5 minutes 27 seconds
==> Builds finished. The artifacts of successful builds are:--> test-packer.amazon-ebs.ubuntu: AMIs were created:us-east-2: ami-0daea26f520e2cfceYour AMI is now available in your AWS account ami_id: ami-0daea26f520e2cfce, in the region us-east-2.
Automate your AMI build with Terraform
module "ec2_instance" { source = "terraform-aws-modules/ec2-instance/aws"
for_each = toset(["dev", "staging", "prod"])
name = "instance-${each.key}"
ami = "ami-0daea26f520e2cfce" instance_type = "t2.micro" key_name = "user1" monitoring = true vpc_security_group_ids = [aws_security_group.nat_instance_sg.id] subnet_id = module.vpc.public_subnets[0]
tags = { Terraform = "true" Environment = "${each.key}" }
depends_on = [ aws_security_group.nat_instance_sg ]}Execute Terraform
Method 1: Using CLI
$ terraform init$ terraform applyMethod 2: Using Github Actions / Gitlab CI / Jenkins
Here is an example of a Gitlab CI/CD to automate the AMI build.
# Define the stagesstages: - init - format - validate - plan - apply
# If the commit starts with "deploy/", then run the pipeline.rules_deploy: &rules_deploy - if: '$CI_COMMIT_TITLE =~ /^deploy/'
# Export AWS credentials.script_export_key: &script_export_key - | export AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} # AWS_ACCESS_KEY_ID is a Gitlab CI/CD variable export AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} # AWS_SECRET_ACCESS_KEY is a Gitlab CI/CD variable
# Define the jobsdefault: image: name: hashicorp/terraform:latest entrypoint: [""] before_script: - *script_export_key
init: stage: init rules: - *rules_deploy script: - terraform init artifacts: # artifacts define, which files will be stored in Gitlab, to download any artifacts you need to use instead 'dependencies: []' because all artifacts are downloaded by default paths: - ".terraform" - ".terraform.lock.hcl" expire_in: 1 day # expire_in define, how long the artifacts will be stored in Gitlab
format: stage: format rules: - *rules_deploy script: - terraform fmt
validate: stage: validate rules: - *rules_deploy script: - terraform validate
plan: stage: plan rules: - *rules_deploy script: - terraform init - terraform plan -out "plan.json" # Save the plan in a file artifacts: paths: - "plan.json" expire_in: 1 day
apply: stage: apply rules: - *rules_deploy script: - terraform init - terraform apply -auto-approve "plan.json" dependencies: - plan when: manual # This job will be executed manually in order to avoid any mistake