Upload Public Key

Introduction

The playbook will upload a key pair from your local machine to AWS account using Ansible. We will use the amazon.aws.ec2_key module. You should have the AWS permissions to import key pairs.

Create the Ansible Playbook

Create a file called upload_key.yml in the ansible/experiments directory.

upload_key.yml
 1---
 2- name: Upload a key to AWS
 3  hosts: localhost
 4  gather_facts: false
 5  vars:
 6    local_pem_path: "/path/to/file.pem" # Update this path to your PEM file
 7    key_name: "RailsKey" # Name for the key pair in AWS
 8
 9  tasks:
10    - name: Read local key file
11      ansible.builtin.slurp:
12        src: "{{ local_pem_path }}"
13      register: pem_file
14
15    - name: Convert key to base64
16      ansible.builtin.set_fact:
17        key_content: "{{ pem_file['content'] | b64decode }}"
18
19    - name: Upload key pair to AWS
20      amazon.aws.ec2_key:
21        name: "{{ key_name }}"
22        key_material: "{{ key_content }}"
23        state: present

Check File Permission

Check PEM file has the correct permission:

PEM file with correct permission
-rw-------@ 1 bparanj  staff  1674 Mar 24 12:14 /path/to/file.pem

Run the Playbook

Copy the path to your PEM file to the line number 6 in upload_key.yml playbook .

From the project root, run the playbook:

Ansible run command
$ ansible-playbook -i hosts experiments/upload_key.yml

Trouble Shooting Issues

Connection Failure

Error: Failed to connect to the host via ssh
PLAY [Upload a key to AWS] 

TASK [Read local key file] 

fatal: [localhost]: UNREACHABLE! => {"changed": false, "msg": "Failed to connect to the host via ssh: ssh: connect to host localhost port 22: Connection refused", "unreachable": true}

PLAY RECAP 

localhost                  : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   

Set connection to local:

upload_key.yml
---
- name: Upload a key to AWS
  hosts: localhost
  gather_facts: false
  connection: local

Key Length Issue

Error due to maximum length allowed for the key:

Error: Length exceeds maximum of 2048.
TASK [Upload key pair to AWS] 
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: Ec2KeyFailure: An error occurred (InvalidParameterValue) when calling the ImportKeyPair operation: Value for parameter PublicKeyMaterial is invalid. Length exceeds maximum of 2048.
fatal: [localhost]: FAILED! => {"boto3_version": "1.34.69", "botocore_version": "1.34.69", "changed": false, "msg": "An error occurred (InvalidParameterValue) when calling the ImportKeyPair operation: Value for parameter PublicKeyMaterial is invalid. Length exceeds maximum of 2048.: error importing key"}

PLAY RECAP 
localhost                  : ok=2    changed=0    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0   
View PEM file
cat /path/to/file.pem

It looks like this:

PEM file contents
-----BEGIN RSA PRIVATE KEY-----
Multiple lines of string
-----END RSA PRIVATE KEY-----%  

We need to extract the public key from the PEM file.

Extract the public key from the PEM file
ssh-keygen -y -f /path/to/file.pem > /path/to/file.pub
View public key
cat /path/to/file.pub

It looks like this:

Public key contents
ssh-rsa smallerstrings goes here

Revised Playbook

Modify the playbook to upload the public key:

upload_key.yml
---
- name: Upload a key to AWS
  hosts: localhost
  gather_facts: false
  connection: local
  vars:
    key_name: "RailsKey" # Name for the key pair in AWS

  tasks:
    - name: Read public key file
      ansible.builtin.slurp:
        src: "/path/to/key.pub" # Update this path to your public key file
      register: public_key_file

    - name: Convert public key to string
      ansible.builtin.set_fact:
        public_key_content: "{{ public_key_file['content'] | b64decode }}"

    - name: Upload public key pair to AWS
      amazon.aws.ec2_key:
        name: "{{ key_name }}"
        key_material: "{{ public_key_content }}"
        state: present

Verify the Key

You can view the uploaded key in your AWS console. Go to EC2 dashboard, pick the region used by the playbook and select Key Pairs under Network and Security. If you don’t see it, check the account used by the playbook:

Check account used to run the playbook
aws sts get-caller-identity

The account ID displayed in the output should match the Account ID shown in your profile of the AWS console.

Using Python SDK

This playbook is useful for learning purposes. The same task is now done by the boto3 Python SDK project. The reason is that we create a key pair and assign it to the instance when it is created. Packer creates a temporary key pair when creating a custom AMI and it deletes it when it is done. So, if you are using Packer, then this is automatically take care of for you.