Install and Setup AWS CLI v2 on Ubuntu 24.04

This post shows a complete, reproducible flow:

  • Install AWS CLI v2 on Ubuntu 24.04 (x86_64)
  • Use AWS CloudShell to create a dedicated IAM user aws-cli-user (programmatic access only) and generate access keys
  • Configure AWS CLI on Ubuntu with aws configure (default region: ap-south-1)

Lab context

  • OS: Ubuntu 24.04
  • Architecture: x86_64
  • Default region: ap-south-1
  • Example S3 bucket: my2-opnsense-imgs (change to your bucket)

  1. Install AWS CLI v2 on Ubuntu 24.04 (x86_64)
  • Install prerequisites
sudo apt update
sudo apt install -y curl unzip
  • Download and install AWS CLI v2 (official ZIP)
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -q awscliv2.zip
sudo ./aws/install
  • Verify
aws --version
which aws

  1. Create IAM user aws-cli-user in AWS (CloudShell)

Important: The following CloudShell script is for this post’s use case (upload a large RAW image to a specific S3 bucket, then run VM Import/Export to create an AMI).
If your use case is different (only S3, only EC2, CloudWatch automation, Route53, etc.), you should change the permissions to match what you actually need.

What this script does (our case)

  • Creates IAM user: aws-cli-user
  • Creates a customer-managed policy that allows the following:
    • Allow uploading large files to one S3 bucket
    • Allow VM Import/Export APIs (import-snapshot, describe tasks)
    • Allow registering an AMI (register-image)
    • Allow creating/updating the vmimport IAM role (one-time setup, done separately)
    • Allow passing the vmimport role to the VM Import/Export service (vmie.amazonaws.com)
  • Generates an access key and saves it to aws-cli-user.access-key.json

Open CloudShell

AWS Console → open CloudShell (top-right terminal icon).

Run the script

USER="aws-cli-user"
BUCKET="my2-opnsense-imgs"
ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text)"
POLICY_NAME="AwsCliUserVmImportS3"

# 1) Create IAM user (if not exists)
aws iam get-user --user-name "$USER" >/dev/null 2>&1 || aws iam create-user --user-name "$USER" >/dev/null
echo "IAM user ready: $USER"

# 2) Write policy document (THIS IS FOR OUR USE CASE; customize for yours)
cat > "/tmp/${POLICY_NAME}.json" <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "S3UploadRawImage",
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation",
        "s3:ListBucket",
        "s3:ListBucketMultipartUploads"
      ],
      "Resource": "arn:aws:s3:::${BUCKET}"
    },
    {
      "Sid": "S3PutObjectMultipart",
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:AbortMultipartUpload",
        "s3:ListMultipartUploadParts"
      ],
      "Resource": "arn:aws:s3:::${BUCKET}/*"
    },
    {
      "Sid": "EC2VmImportAndRegisterAmi",
      "Effect": "Allow",
      "Action": [
        "ec2:ImportSnapshot",
        "ec2:DescribeImportSnapshotTasks",
        "ec2:DescribeSnapshots",
        "ec2:RegisterImage",
        "ec2:DescribeImages",
        "ec2:Describe*"
      ],
      "Resource": "*"
    },
    {
      "Sid": "CreateAndManageVmImportRole",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:PutRolePolicy",
        "iam:GetRole",
        "iam:GetRolePolicy",
        "iam:ListRolePolicies",
        "iam:UpdateAssumeRolePolicy"
      ],
      "Resource": "*"
    },
    {
      "Sid": "PassVmImportRoleToVmie",
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": "arn:aws:iam::${ACCOUNT_ID}:role/vmimport",
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "vmie.amazonaws.com"
        }
      }
    }
  ]
}
EOF

# 3) Create customer-managed policy (if not exists)
POLICY_ARN="$(aws iam list-policies --scope Local \
  --query "Policies[?PolicyName=='${POLICY_NAME}'].Arn | [0]" --output text)"

if [ "$POLICY_ARN" = "None" ] || [ -z "$POLICY_ARN" ]; then
  POLICY_ARN="$(aws iam create-policy \
    --policy-name "$POLICY_NAME" \
    --policy-document "file:///tmp/${POLICY_NAME}.json" \
    --query 'Policy.Arn' --output text)"
  echo "Created policy: $POLICY_ARN"
else
  echo "Policy already exists: $POLICY_ARN"
fi

# 4) Attach policy to user
aws iam attach-user-policy --user-name "$USER" --policy-arn "$POLICY_ARN"
echo "Attached policy to user."

# 5) Create access key (SECRET shown once!) and save to a file
aws iam create-access-key --user-name "$USER" \
  --query 'AccessKey.{AccessKeyId:AccessKeyId,SecretAccessKey:SecretAccessKey}' \
  --output json > "${USER}.access-key.json"

echo "Access key saved to: ${USER}.access-key.json"
echo "IMPORTANT: Download it and delete it from CloudShell afterwards."

Download and delete the access key file

Download aws-cli-user.access-key.json from CloudShell, then remove it:

rm -f aws-cli-user.access-key.json

  1. Configure AWS CLI on Ubuntu (aws configure)

On Ubuntu, run:

aws configure

Enter:

  • AWS Access Key ID: from aws-cli-user.access-key.json
  • AWS Secret Access Key: from aws-cli-user.access-key.json
  • Default region name: ap-south-1
  • Default output format: json

Confirm your config:

ls -la ~/.aws
cat ~/.aws/credentials
cat ~/.aws/config

Test credentials:

aws sts get-caller-identity

Did this guide save you time?

Support this site

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top