AWS - using an access key, AMI templates, disk snapshots to safely upgrade software

Introduction
Creating an account and access key
Creating a disk snapshot
Upgrading linux software with dpkg tool
Using AMI templates and restoring a disk snapshot to an EC2 instance
Removing AMI templates and disk snapshots

Introduction

Let’s say we want to upgrade the software on a linux VM on AWS EC2, but first we would like to back up the VM’s boot disk as a snapshot, in case the upgrade causes issues and we want to restore from backup.
We will use the AWS web console to create a user in IAM, and we will use the AWS CLI to take a snapshot of the VM’s disk and restore it, if needed.

Creating an account and access key

In IAM we will create a new user, to which we will assign permissions, and an access key to be used in the AWS CLI.

  1. Go to IAM > Users > Create user Name the user george-aws2-sa-2 or similar. Do not tick Provide user access to AWS console, as we will use the AWS CLI for commands in the console/terminal only.
  2. In the next screen, choose Attach policies directly and tick the policy AmazonEC2FullAccess, to manage access to EC2 instances and disks.
  3. Scroll at the bottom of the page, choose Next, then Create user.
  4. In the user screen, click Create access key, choose Command line interface, click Next and Create access key.
  5. Copy both the Access key and Secret access key. You will be add them to the .aws/credentials file in your home folder:
[aws-sa-2]
aws_access_key_id = AKIxxxE
aws_secret_access_key = f23xxxe

Creating a disk snapshot

Let’s list the VMs in the eu-west-2 region and their attached disk volumes using aws ec2 describe-instances; we will use the --profile aws-sa-2 option to select the new AWS CLI profile:

$ aws --profile aws-sa-2 ec2 describe-instances \
    --region eu-west-2 \
    --query 'Reservations[].Instances[].{InstanceId: InstanceId, State: State.Name, LaunchTime: LaunchTime, Volumes: BlockDeviceMappings[].Ebs.VolumeId}' \
    --output table
-----------------------------------------------------------------
|                       DescribeInstances                       |
+----------------------+-----------------------------+----------+
|      InstanceId      |         LaunchTime          |  State   |
+----------------------+-----------------------------+----------+
|  i-08dc2f13b35f74c25 |  2025-04-05T20:18:47+00:00  |  running |
+----------------------+-----------------------------+----------+
||                           Volumes                           ||
|+-------------------------------------------------------------+|
||  vol-047029f1d13f42d7b                                      ||
|+-------------------------------------------------------------+|

To create a snapshot of the disk from the output above, we will use aws ec2 create-snapshot; we will add the tag WebServerDataBackup and the current date:

aws ec2 create-snapshot \
    --volume-id vol-047029f1d13f42d7b \
    --description "Backup of production web server data volume 2025-05-10" \
    --region eu-west-2 \
    --tag-specifications 'ResourceType=snapshot,Tags=[{Key=Name,Value=WebServerDataBackup},{Key=Date,Value=2025-05-10}]' \
    --profile aws-sa-2 # Include if you need a specific profile

The result:

{
    "Tags": [
        {
            "Key": "Name",
            "Value": "WebServerDataBackup"
        },
        {
            "Key": "Date",
            "Value": "2025-05-10"
        }
    ],
    "SnapshotId": "snap-092a608a99f28238f",
    "VolumeId": "vol-047029f1d13f42d7b",
    "State": "pending",
    "StartTime": "2025-05-10T13:24:21.404000+00:00",
    "Progress": "",
    "OwnerId": "940767832355",
    "Description": "Backup of production web server data volume 2025-05-10",
    "VolumeSize": 8,
    "Encrypted": false
}

We will use aws ec2 describe-snapshots to list the snapshots in the current AWS account:

aws ec2 describe-snapshots \
    --owner-ids self \
    --region eu-west-2 \
    --query 'Snapshots[*].{ID:SnapshotId,VolumeId:VolumeId,Size:VolumeSize,State:State,StartTime:StartTime,Description:Description}' \
    --output table \
    --profile aws-sa-2

The output of the command:

--------------------------------------------------------------------------------------------------------------
|                                              DescribeSnapshots                                             |
+------------------------+-------+------------------------------------+------------+-------------------------+
|           ID           | Size  |             StartTime              |   State    |        VolumeId         |
+------------------------+-------+------------------------------------+------------+-------------------------+
|  snap-092a608a99f28238f|  8    |  2025-05-10T13:24:21.404000+00:00  |  completed |  vol-047029f1d13f42d7b  |
+------------------------+-------+------------------------------------+------------+-------------------------+

Upgrading linux software with dpkg tool

Now let’s install a new version of the Hugo static website generator. Let’s get the current version.

$ hugo version
hugo v0.139.3-2f6864387cd31b975914e8373d4bf38bddbd47bc+extended linux/amd64 BuildDate=2024-11-29T15:36:56Z VendorInfo=gohugoio

We can use wget to download the latest version from Github.

$ wget https://github.com/gohugoio/hugo/releases/download/v0.147.2/hugo_extended_0.147.2_linux-amd64.deb
[..]
Connecting to objects.githubusercontent.com (objects.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 19229722 (18M) [application/octet-stream]
Saving to: ‘hugo_extended_0.147.2_linux-amd64.deb’

hugo_extended_0.147.2_linux-amd64.deb           100%[==========================>]  18.34M  73.6MB/s    in 0.2s

2025-05-10 13:33:32 (73.6 MB/s) - ‘hugo_extended_0.147.2_linux-amd64.deb’ saved [19229722/19229722]

We will use the dpkg Debian package tool to install the new version:

$ sudo dpkg -i hugo_extended_0.147.2_linux-amd64.deb
(Reading database ... 46521 files and directories currently installed.)
Preparing to unpack hugo_extended_0.147.2_linux-amd64.deb ...
Unpacking hugo (0.147.2) over (0.139.3) ...
Setting up hugo (0.147.2) ...

Let’s check, the new hugo runtime version is installed:

$ hugo version
hugo v0.147.2-c7feb15d10b29a94d7fb57c31e8bcb2e92718fb7+extended linux/amd64 BuildDate=2025-05-06T11:18:55Z VendorInfo=gohugoio

Using AMI templates and restoring a disk snapshot to an EC2 instance

If we want to restore the EC2 VM from the snapshot, we need to create an AMI, a template that contains references to the disk snapshot, network configuration, etc..
We use aws ec2 register-image to register an AMI image, based on the disk snapshot:

$ aws ec2 register-image \
    --profile aws-sa-2 \
    --region eu-west-2 \
    --name "RestoredAMI" \
    --root-device-name /dev/sda1 \
    --block-device-mappings "[{\"DeviceName\":\"/dev/sda1\",\"Ebs\":{\"SnapshotId\":\"snap-092a608a99f28238f\"}}]"

The result is the ID of the AMI template:

{
    "ImageId": "ami-0d0a76bd8e71c09df"
}

To create a new EC2 instance, we will need more information. First, here is the complete command:

aws ec2 run-instances \
    --profile aws-sa-2 \
    --region eu-west-2 \
    --image-id ami-xxx \
    --instance-type t2.micro \
    --key-name ssh-keypair-name \
    --security-group-ids sg-xx \
    --subnet-id subnet-tt

To get the instance type:

$ aws --profile aws-sa-2 ec2 describe-instances \
    --region eu-west-2 \
    --query 'Reservations[].Instances[].{InstanceId: InstanceId, InstanceType: InstanceType, SubnetId: NetworkInterfaces[0].SubnetId}' \
    --output table
---------------------------------------------------------------------
|                         DescribeInstances                         |
+----------------------+---------------+----------------------------+
|      InstanceId      | InstanceType  |         SubnetId           |
+----------------------+---------------+----------------------------+
|  i-08dc2f13b35f74c25 |  t2.micro     |  subnet-08b55484cbc415578  |
+----------------------+---------------+----------------------------+

To get the SSH key name, let’s use aws ec2 describe-key-pairs:

$ aws ec2 describe-key-pairs \
    --profile aws-sa-2 \
    --region eu-west-2 \
    --query "KeyPairs[*].[KeyName, KeyFingerprint]" \
    --output table
----------------------------------------------------------------------
|                          DescribeKeyPairs                          |
+-------------------+------------------------------------------------+
|  my-aws2-keypair1 |  yy/xx=                                        |
+-------------------+------------------------------------------------+

To get the security groups attached to an EC2 instance:

$ aws --profile aws-sa-2 ec2 describe-instances \
    --region eu-west-2 \
    --query 'Reservations[].Instances[].{InstanceId: InstanceId, SecurityGroups: SecurityGroups[].{GroupId: GroupId, GroupName: GroupName}}' \
    --output table
-----------------------------------------------
|              DescribeInstances              |
+---------------------------------------------+
|                 InstanceId                  |
+---------------------------------------------+
|  i-08dc2f13b35f74c25                        |
+---------------------------------------------+
||              SecurityGroups               ||
|+-----------------------+-------------------+|
||        GroupId        |     GroupName     ||
|+-----------------------+-------------------+|
||  sg-0744115e5d494da09 |  launch-wizard-1  ||
|+-----------------------+-------------------+|

To get the subnet ID:

$ aws --profile aws-sa-2 ec2 describe-instances \
    --region eu-west-2 \
    --query 'Reservations[].Instances[].{InstanceId: InstanceId, SubnetId: NetworkInterfaces[0].SubnetId}' \
    --output table
-----------------------------------------------------
|                 DescribeInstances                 |
+----------------------+----------------------------+
|      InstanceId      |         SubnetId           |
+----------------------+----------------------------+
|  i-08dc2f13b35f74c25 |  subnet-08b55484cbc415578  |
+----------------------+----------------------------+

Removing AMI templates and disk snapshots

To deregister the AMI template, if not needed:

$ aws --profile aws-sa-2 ec2 deregister-image \
    --image-id ami-0d0a76bd8e71c09df \
    --region eu-west-2

To delete the disk snapshot, if not needed:

$ aws --profile aws-sa-2 ec2 delete-snapshot \
    --snapshot-id snap-092a608a99f28238f \
    --region eu-west-2