In this exercise we’ll use Ansible and automate some common tasks with Google Cloud Platform.

What if you want to save some bucks and automatically start & stop all of your GCP instances when you’re working? The answer is simple: Ansible!

Getting Started

First thing is first, let’s use a virtualenv and install ansible:

$ python3 -m venv .
$ source bin/activate
$ pip install apache-libcloud pycrypto ansible

Second, you’ll need to create a Service Account Key and download it locally. (See: Creating and Managing Service Account Keys)

Create our first Playbook

A “Playbook” is simply a logical grouping of instructions (or “plays”).

gce-instances-create.yaml
- name: Create instances ("{{ instances }}")
  hosts: localhost
  connection: local
  gather_facts: no

  vars:
    service_account_email: [email protected]
    credentials_file: /some/path/yourkey.json
    project_id: your-project-id
    machine_type: n1-standard-1
    image: centos-7

  tasks:
    - name: Launch instances ("{{ instances }}")
      gce:
          instance_names: "{{ instances }}"
          machine_type: "{{ machine_type }}"
          image: "{{ image }}"
          service_account_email: "{{ service_account_email }}"
          credentials_file: "{{ credentials_file }}"
          project_id: "{{ project_id }}"
          disk_auto_delete: true
          preemptible: true
          tags: ansible
      register: gce

    - name: Wait for SSH to come up ("{{ instances }}")
      wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=60
      with_items: "{{ gce.instance_data }}"

    - name: Add host to groupname ("{{ instances }}")
      add_host: hostname={{ item.public_ip }} groupname=new_instances
      with_items: "{{ gce.instance_data }}"
    
    - name: Save host data ("{{ instances }}")
      add_host:
        hostname: "{{ item.public_ip }}"
        groupname: gce_instances
      with_items: "{{ gce.instance_data }}"

Running the Playbook

To run our playbook we’ll use the ansible-playbook command:

$ ansible-playbook -e instances="test-1 test-2" gce-instances-create.yml

Notice that we’re passing the names of the instances to create as an environment variable with the -e instances=”..” switch.

Here is the output:

PLAY [Create instances ("test-1")] ********************************************************************************************************************************************************************************

TASK [Launch instances ("test-1")] ********************************************************************************************************************************************************************************
changed: [localhost]

TASK [Wait for SSH to come up ("test-1")] *************************************************************************************************************************************************************************
ok: [localhost] => (item={'image': 'centos-7-v20181210', 'disks': ['test-1'], 'machine_type': 'n1-standard-1', 'metadata': {}, 'name': 'test-1', 'network': 'default', 'subnetwork': 'default', 'private_ip': '10.128.0.2', 'public_ip': '23.251.150.222', 'status': 'RUNNING', 'tags': ['ansible'], 'zone': 'us-central1-a'})

TASK [Add host to groupname ("test-1")] ***************************************************************************************************************************************************************************
changed: [localhost] => (item={'image': 'centos-7-v20181210', 'disks': ['test-1'], 'machine_type': 'n1-standard-1', 'metadata': {}, 'name': 'test-1', 'network': 'default', 'subnetwork': 'default', 'private_ip': '10.128.0.2', 'public_ip': '23.251.150.222', 'status': 'RUNNING', 'tags': ['ansible'], 'zone': 'us-central1-a'})

TASK [Save host data ("test-1")] **********************************************************************************************************************************************************************************
changed: [localhost] => (item={'image': 'centos-7-v20181210', 'disks': ['test-1'], 'machine_type': 'n1-standard-1', 'metadata': {}, 'name': 'test-1', 'network': 'default', 'subnetwork': 'default', 'private_ip': '10.128.0.2', 'public_ip': '23.251.150.222', 'status': 'RUNNING', 'tags': ['ansible'], 'zone': 'us-central1-a'})

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=4    changed=3    unreachable=0    failed=0

Removing the new instances

We’ll use the following playbook.. notice the state: absent instruction at the end. This will tell the gce ansible module to delete these instances:

- name: Delete instance(s)
  hosts: localhost
  connection: local
  gather_facts: no

  vars:
    service_account_email: [email protected]ount.com
    credentials_file: /some/path/yourkey.json
    project_id: your-project-id 
    zone: us-central1-a

  tasks:

    - name: Destroy instances

      gce:
        instance_names: "{{ instances }}"
        zone: "{{ zone }}"
        project_id: "{{ project_id }}"
        credentials_file: "{{ credentials_file }}"
        service_account_email: "{{ service_account_email }}"
        state: absent

Run the playbook with:

$ ansible-playbook -e instances="test-1 test-2" gce-instances-delete.yml

So with these two commands you can easily enough spin up your infrastructure and clean up when you’re done saving you money and time!

See Also