How to configure OS Login in GCP for Ansible

May 18, 2019

Recently I started to work with Google Cloud and port some of our infrastructure from metal datacenter to the cloud environment. As an intermediate step, I use Compute Engine instances as servers to host Consul, Prometheus, Zookeeper and other stuff that I have in datacenter. I do this exclusively to maintain production environment parity where infrastructure is managed by Ansible.

This is where SSH access to instances for Ansible is needed. There are 2 ways that this could be accomplished - 1) Add SSH key to the project metadata 2) Use OS Login feature. As you can guess I’m using OS Login. You can read about OS Login and its benefits in docs. Here I’ll show you how to make Ansible work via OS Login.

In the end, we’ll have a service account for Ansible that will be able to SSH connect to instances via OS login.

Service account

In short, OS Login allows SSH access for IAM users - there is no need to provision Linux users on an instance.

So Ansible should have access to the instances via IAM user. This is accomplished via IAM service account.

You can create service account via Console (web UI), via Terraform template or (as in my case) via gcloud:

$ gcloud iam service-accounts create ansible-sa \
     --display-name "Service account for Ansible"

Configure OS Login

Now, the trickiest part – configuring OS Login for service account.

1. Add roles

Fresh service account doesn’t have any IAM roles so it doesn’t have permission to do anything. To allow OS Login we have to add these 4 roles to the Ansible service account:

  • Compute Instance Admin (beta)
  • Compute Instance Admin (v1)
  • Compute OS Admin Login
  • Service Account User

Here is how to do it via gcloud:

for role in \
    'roles/compute.instanceAdmin' \
    'roles/compute.instanceAdmin.v1' \
    'roles/compute.osAdminLogin' \
    'roles/iam.serviceAccountUser'
do \
    gcloud projects add-iam-policy-binding \
        my-gcp-project-241123 \
        --member='serviceAccount:ansible-sa@my-gcp-project-241123.iam.gserviceaccount.com' \
        --role="${role}"
done

2. Create key for service account and save it

Service account is useless without key, create one with gcloud:

$ gcloud iam service-accounts keys create \
    .gcp/gcp-key-ansible-sa.json \
    --iam-account=ansible-sa@my-gcp-project.iam.gserviceaccount.com

This will create GCP key, not the SSH key. This key is used for interacting with Google Cloud API – tools like gcloud, gsutil and others are using it. We will need this key for gcloud to add SSH key to the service account.

3. Create SSH key for service account

This is the easiest part)

$ ssh-keygen -f ssh-key-ansible-sa

4. Add SSH key for OS login to service account

Now, to allow service account to access instances via SSH it has to have SSH key added to it. To do this, first, we have to activate service account in gcloud:

$ gcloud auth activate-service-account \
    --key-file=.gcp/gcp-key-ansible-sa.json

This command uses GCP key we’ve created on step 2.

Now we add SSH key to the service account:

$ gcloud compute os-login ssh-keys add \
    --key-file=ssh-key-ansible-sa.pub

5. Switch back from service account

$ gcloud config set account your@gmail.com

Connecting to the instance with OS login

Now, we have everything configured on the GCP side, we can check that it’s working.

Note, that you don’t need to add SSH key to compute metadata, authentication works via OS login. But this means that you need to know a special user name for the service account.

Find out the service account id.

$ gcloud iam service-accounts describe \
    ansible-sa@my-gcp-project.iam.gserviceaccount.com \
    --format='value(uniqueId)'
106627723496398399336

This id is used to form user name in OS login – it’s sa_<unique_id>.

Here is how to use it to check SSH access is working:

$ ssh -i .ssh/ssh-key-ansible-sa sa_106627723496398399336@10.0.0.44
...

sa_106627723496398399336@instance-1:~$ # Yay!

Configuring Ansible

And for the final part – make Ansible work with it.

There is a special variable ansible_user that sets user name for SSH when Ansible connects to the host.

In my case, I have a group gcp where all GCP instances are added, and so I can set ansible_user in group_vars like this:

# File inventory/dev/group_vars/gcp
ansible_user: sa_106627723496398399336

And check it:

$ ansible -i inventory/dev gcp -m ping
10.0.0.44 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}
10.0.0.43 | SUCCESS => {
    "changed": false, 
    "ping": "pong"
}

And now we have Ansible configured to access GCP instances via OS Login. There is no magic here – just a bit of gluing together a bunch of stuff after reading lots of docs. That’s it for now, till the next time!