Google Cloud Platform Guide

Introduction

Note

This section of the documentation is under construction. We are in the process of adding more examples about all of the GCE modules and how they work together. Upgrades via github pull requests are welcomed!

Ansible contains modules for managing Google Compute Engine resources, including creating instances, controlling network access, working with persistent disks, and managingload balancers. Additionally, there is an inventory plugin that can automatically suck down all of your GCE instances into Ansible dynamic inventory, and create groups by tag and other properties.

The GCE modules all require the apache-libcloud module, which you can install from pip:

  1. $ pip install apache-libcloud

Note

If you’re using Ansible on Mac OS X, libcloud also needs to access a CA cert chain. You’ll need to download one (you can get one for here.)

Credentials

To work with the GCE modules, you’ll first need to get some credentials. You can create new one from the console by going to the “APIs and Auth” section and choosing to create a new client ID for a service account. Once you’ve created a new client ID and downloaded (you must click Generate new P12 Key) the generated private key (in the pkcs12 format), you’ll need to convert the key by running the following command:

  1. $ openssl pkcs12 -in pkey.pkcs12 -passin pass:notasecret -nodes -nocerts | openssl rsa -out pkey.pem

There are two different ways to provide credentials to Ansible so that it can talk with Google Cloud for provisioning and configuration actions:

  • by providing to the modules directly
  • by populating a secrets.py file

Calling Modules By Passing Credentials

For the GCE modules you can specify the credentials as arguments:

  • service_account_email: email associated with the project
  • pem_file: path to the pem file
  • project_id: id of the project

For example, to create a new instance using the cloud module, you can use the following configuration:

  1. - name: Create instance(s)
  2. hosts: localhost
  3. connection: local
  4. gather_facts: no
  5.  
  6. vars:
  7. service_account_email: unique-id@developer.gserviceaccount.com
  8. pem_file: /path/to/project.pem
  9. project_id: project-id
  10. machine_type: n1-standard-1
  11. image: debian-7
  12.  
  13. tasks:
  14.  
  15. - name: Launch instances
  16. gce:
  17. instance_names: dev
  18. machine_type: "{{ machine_type }}"
  19. image: "{{ image }}"
  20. service_account_email: "{{ service_account_email }}"
  21. pem_file: "{{ pem_file }}"
  22. project_id: "{{ project_id }}"

Calling Modules with secrets.py

Create a file secrets.py looking like following, and put it in some folder which is in your $PYTHONPATH:

  1. GCE_PARAMS = ('i...@project.googleusercontent.com', '/path/to/project.pem')
  2. GCE_KEYWORD_PARAMS = {'project': 'project_id'}

Ensure to enter the email address from the created services account and not the one from your main account.

Now the modules can be used as above, but the account information can be omitted.

GCE Dynamic Inventory

The best way to interact with your hosts is to use the gce inventory plugin, which dynamically queries GCE and tells Ansible what nodes can be managed.

Note that when using the inventory script gce.py, you also need to populate the gce.ini file that you can find in the contrib/inventory directory of the ansible checkout.

To use the GCE dynamic inventory script, copy gce.py from contrib/inventory into your inventory directory and make it executable. You can specify credentials for gce.py using the GCE_INI_PATH environment variable – the default is to look for gce.ini in the same directory as the inventory script.

Let’s see if inventory is working:

  1. $ ./gce.py --list

You should see output describing the hosts you have, if any, running in Google Compute Engine.

Now let’s see if we can use the inventory script to talk to Google.

  1. $ GCE_INI_PATH=~/.gce.ini ansible all -i gce.py -m setup
  2. hostname | success >> {
  3. "ansible_facts": {
  4. "ansible_all_ipv4_addresses": [
  5. "x.x.x.x"
  6. ],

As with all dynamic inventory scripts in Ansible, you can configure the inventory path in ansible.cfg. The recommended way to use the inventory is to create an inventory directory, and place both the gce.py script and a file containing localhost in it. This can allow for cloud inventory to be used alongside local inventory (such as a physical datacenter) or machines running in different providers.

Executing ansible or ansible-playbook and specifying the inventory directory instead of an individual file will cause ansible to evaluate each file in that directory for inventory.

Let’s once again use our inventory script to see if it can talk to Google Cloud:

  1. $ ansible all -i inventory/ -m setup
  2. hostname | success >> {
  3. "ansible_facts": {
  4. "ansible_all_ipv4_addresses": [
  5. "x.x.x.x"
  6. ],

The output should be similar to the previous command. If you’re wanting less output and just want to check for SSH connectivity, use “-m” ping instead.

Use Cases

For the following use case, let’s use this small shell script as a wrapper.

  1. #!/usr/bin/env bash
  2. PLAYBOOK="$1"
  3.  
  4. if [[ -z $PLAYBOOK ]]; then
  5. echo "You need to pass a playbook as argument to this script."
  6. exit 1
  7. fi
  8.  
  9. export SSL_CERT_FILE=$(pwd)/cacert.cer
  10. export ANSIBLE_HOST_KEY_CHECKING=False
  11.  
  12. if [[ ! -f "$SSL_CERT_FILE" ]]; then
  13. curl -O http://curl.haxx.se/ca/cacert.pem
  14. fi
  15.  
  16. ansible-playbook -v -i inventory/ "$PLAYBOOK"

Create an instance

The GCE module provides the ability to provision instances within Google Compute Engine. The provisioning task is typically performed from your Ansible control server against Google Cloud’s API.

A playbook would looks like this:

  1. - name: Create instance(s)
  2. hosts: localhost
  3. gather_facts: no
  4. connection: local
  5.  
  6. vars:
  7. machine_type: n1-standard-1 # default
  8. image: debian-7
  9. service_account_email: unique-id@developer.gserviceaccount.com
  10. pem_file: /path/to/project.pem
  11. project_id: project-id
  12.  
  13. tasks:
  14. - name: Launch instances
  15. gce:
  16. instance_names: dev
  17. machine_type: "{{ machine_type }}"
  18. image: "{{ image }}"
  19. service_account_email: "{{ service_account_email }}"
  20. pem_file: "{{ pem_file }}"
  21. project_id: "{{ project_id }}"
  22. tags: webserver
  23. register: gce
  24.  
  25. - name: Wait for SSH to come up
  26. wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=60
  27. with_items: gce.instance_data
  28.  
  29. - name: Add host to groupname
  30. add_host: hostname={{ item.public_ip }} groupname=new_instances
  31. with_items: gce.instance_data
  32.  
  33. - name: Manage new instances
  34. hosts: new_instances
  35. connection: ssh
  36. sudo: True
  37. roles:
  38. - base_configuration
  39. - production_server

Note that use of the “add_host” module above creates a temporary, in-memory group. This means that a play in the same playbook can then manage machinesin the ‘new_instances’ group, if so desired. Any sort of arbitrary configuration is possible at this point.

Configuring instances in a group

All of the created instances in GCE are grouped by tag. Since this is a cloud, it’s probably best to ignore hostnames and just focus on group management.

Normally we’d also use roles here, but the following example is a simple one. Here we will also use the “gce_net” module to open up access to port 80 onthese nodes.

The variables in the ‘vars’ section could also be kept in a ‘vars_files’ file or something encrypted with Ansible-vault, if you so choose. This is justa basic example of what is possible:

  1. - name: Setup web servers
  2. hosts: tag_webserver
  3. gather_facts: no
  4.  
  5. vars:
  6. machine_type: n1-standard-1 # default
  7. image: debian-7
  8. service_account_email: unique-id@developer.gserviceaccount.com
  9. pem_file: /path/to/project.pem
  10. project_id: project-id
  11.  
  12. roles:
  13.  
  14. - name: Install lighttpd
  15. apt: pkg=lighttpd state=installed
  16. sudo: True
  17.  
  18. - name: Allow HTTP
  19. local_action: gce_net
  20. args:
  21. fwname: "all-http"
  22. name: "default"
  23. allowed: "tcp:80"
  24. state: "present"
  25. service_account_email: "{{ service_account_email }}"
  26. pem_file: "{{ pem_file }}"
  27. project_id: "{{ project_id }}"

By pointing your browser to the IP of the server, you should see a page welcoming you.

Upgrades to this documentation are welcome, hit the github link at the top right of this page if you would like to make additions!