This is a text-only version of the following page on https://raymii.org: --- Title : Ansible - Create OpenStack servers with Ansible 2.0 and the os_server module and a dynamic inventory Author : Remy van Elst Date : 10-09-2016 URL : https://raymii.org/s/tutorials/Ansible_-_create_OpenStack_servers_with_os_server.html Format : Markdown/HTML --- I regularly deploy clusters and single servers on OpenStack with Ansible. However, Ansible 2.0 comes with new OpenStack modules my playbooks still used the old ones. I reserved some time to convert these playbooks to the new modules and ansible 2. This article shows a very simple example, it creates three servers in OpenStack and adds them to different hostgroups based on variables. For example, to create one loadbalancer and two appservers and run specific playbooks on those hosts based on their role. Since Ansible 2 the OpenStack modules are renewed. The old `nova_*` modules are replaced by the `os_server` modules. Ansible 2 also changed some deperecated stuff regarding `yaml` parsing and variable concatination. Therefore some of my old playbooks were not working anymore and I had to figure out how to get it working with the new versions. I've written about [Ansible before][1], click that link to get all the articles about Ansible. ![ansible][2] <p class="ad"> <b>Recently I removed all Google Ads from this site due to their invasive tracking, as well as Google Analytics. Please, if you found this content useful, consider a small donation using any of the options below:</b><br><br> <a href="https://leafnode.nl">I'm developing an open source monitoring app called Leaf Node Monitoring, for windows, linux & android. Go check it out!</a><br><br> <a href="https://github.com/sponsors/RaymiiOrg/">Consider sponsoring me on Github. It means the world to me if you show your appreciation and you'll help pay the server costs.</a><br><br> <a href="https://www.digitalocean.com/?refcode=7435ae6b8212">You can also sponsor me by getting a Digital Ocean VPS. With this referral link you'll get $100 credit for 60 days. </a><br><br> </p> I [found][4] documentation on the new modules but that just created a single instance. I need to spawn multiple instances and add those to specific hostgroups based on variables, so this guide wasn't complete enough for me. This is my folder structure: $ tree -I "*.git" . |-- ansible.cfg |-- group_vars | `-- all.yml |-- roles | |-- haproxy | | |-- handlers | | | `-- main.yml | | |-- tasks | | | `-- main.yml | | |-- templates | | | `-- haproxy.cfg.j2 | | `-- vars | | `-- main.yml | `-- create_instances | |-- tasks | | `-- main.yml | `-- vars | `-- main.yml `-- site.yml `site.yml` is the main playbook and `create-instances` and `appservers` are the specific roles. [Create][5] the folder structure if you're starting from zero. On the local host you need the OpenStack tools and some python modules installed. The [os_server][6] page lists all the requirements. You also need an `openstackrc` file with credentials in your environment: $ cat openstackrc export OS_AUTH_URL="https://identity.stack.cloudvps.com/v2.0" export OS_USERNAME="username" export OS_PASSWORD="password" export OS_TENANT_ID="UUID" export OS_TENANT_NAME="UUID" Source it before running the playbooks: source openstackrc ### Main playbook The `site.yml` playbook first runs on `locahost` and creates the OpenStack instances. It also adds them to the specific hostgroups. Those hostgroups are only available when you run this playbook. Then it starts a new `play` (if that is how you call multiple runs) to the first newly created hostgroup (loadbalancers) and you can add a new `play` to run on the appservers as well. --- - name: create instances hosts: localhost roles: - { role: create_instances } - name: deploy haproxy hosts: loadbalancers user: root roles: - { role: haproxy } - { role: keepalived } ### Instance creation playbook The first playbook, `roles/create-instances/tasks/main.yml` runs on localhost and creates the OpenStack instances: --- - name: launch instances os_server: name: "{{ prefix }}-{{ item.name }}" state: present key_name: "{{ item.key }}" availability_zone: "{{ item.availability_zone }}" nics: "{{ item.nics }}" image: "{{ item.image }}" flavor: "{{ item.flavor }}" with_items: "{{ servers }}" register: "os_hosts" - name: add hosts to inventory add_host: name: "{{ item['openstack']['human_id'] }}" groups: "{{ item['item']['meta']['group'] }}" ansible_host: "{{ item.openstack.accessIPv4 }}" with_items: "{{ os_hosts.results }}" Based on the name we defined and the group we gave it also is adds the hosts to a new hostgroup. That hostgroup is only active within this playbook run. The parameters we used to create the instances are also available in the `result` of that action. It's a `dict`, so you can access all three the servers we created. That is were we get the `group` value from. If you're using a `jumphost` and other machines with private IP's you need to use a different `ansible_ssh_host`, but you can achieve that by adding extra data to the variables. The `debug` module is your friend here. If the instances are already created, it will not create them again but it will add then to the hostgroup again. The variables required for this playbook are the following (`roles/create- instances/vars/main.yml`): --- prefix: demo servers: - name: lb1 image: CloudVPS Ubuntu 16.04 flavor: Standard 2 key: SSH-Key nics: "net-id=00000000-0000-0000-0000-000000000000" availability_zone: NL1 meta: group: loadbalancers - name: app1 image: CloudVPS Ubuntu 16.04 flavor: Standard 2 key: SSH-Key nics: "net-id=00000000-0000-0000-0000-000000000000" availability_zone: NL1 meta: group: appservers - name: app2 image: CloudVPS Ubuntu 16.04 flavor: Standard 2 key: SSH-Key nics: "net-id=00000000-0000-0000-0000-000000000000" availability_zone: NL2 meta: group: appservers The `prefix` is used in the servername and can be used to create different pseudo-groups in the same tenant/project. I do recommend to create different projects/tenants per environment (accept/staging etc) instead of prefixes, since you can then manage the rights more fine grained. ### Role specific playbooks The role specific playbooks are just regular playbook roles you would run. For the guide you can use an example play which just does a ping (`roles/ping/tasks/main.yml`): --- - name: ping instances ping: The full play then results in: $ ansible-playbook site.yml [WARNING]: provided hosts list is empty, only localhost is available PLAY [create instances] ******************************************************** TASK [setup] ******************************************************************* ok: [localhost] TASK [create_instances : launch instances] ************************************* changed: [localhost] => (item={u'name': u'lb1', u'availability_zone': u'NL1', u'nics': u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS Ubuntu 16.04', u'meta': {u'group': u'loadbalancers'}, u'key': u'SSH-Key', u'flavor': u'Standard 2'}) changed: [localhost] => (item={u'name': u'app1', u'availability_zone': u'NL1', u'nics': u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS Ubuntu 16.04', u'meta': {u'group': u'appservers'}, u'key': u'SSH-Key', u'flavor': u'Standard 2'}) changed: [localhost] => (item={u'name': u'app2', u'availability_zone': u'NL1', u'nics': u'net-id=00000000-0000-0000-0000-000000000000', u'image': u'CloudVPS Ubuntu 16.04', u'meta': {u'group': u'appservers'}, u'key': u'SSH-Key', u'flavor': u'Standard 2'}) TASK [create_instances : add hosts to inventory] ******************************* changed: [localhost] => (item={u'changed': True, '_ansible_no_log': False, '_ansible_item_result': True, u'server': [...] # a lot of json u'volumes': [], u'metadata': {}, u'human_id': u'demo-app2'}, u'id': u'eff00345-977f-4c72-4684-4aa22d1dfc9f'}) PLAY [ping instances] ********************************************************** TASK [setup] ******************************************************************* ok: [demo-app1] ok: [demo-app2] TASK [common : ping instances] ************************************************* ok: [demo-app1] ok: [demo-app2] PLAY RECAP ********************************************************************* demo-app1 : ok=2 changed=0 unreachable=0 failed=0 demo-app2 : ok=2 changed=0 unreachable=0 failed=0 localhost : ok=3 changed=2 unreachable=0 failed=0 [1]: https://raymii.org/s/tags/ansible.html [2]: https://raymii.org/s/inc/img/ansible_meme.jpg [3]: https://www.digitalocean.com/?refcode=7435ae6b8212 [4]: http://blog.oddbit.com/2015/10/26/ansible-20-new-openstack-modules/ [5]: https://raymii.org/s/snippets/Ansible_-_create_playbooks_and_role_file_and_folder_structure.html [6]: https://docs.ansible.com/ansible/os_server_module.html --- License: All the text on this website is free as in freedom unless stated otherwise. This means you can use it in any way you want, you can copy it, change it the way you like and republish it, as long as you release the (modified) content under the same license to give others the same freedoms you've got and place my name and a link to this site with the article as source. This site uses Google Analytics for statistics and Google Adwords for advertisements. You are tracked and Google knows everything about you. Use an adblocker like ublock-origin if you don't want it. All the code on this website is licensed under the GNU GPL v3 license unless already licensed under a license which does not allows this form of licensing or if another license is stated on that page / in that software: This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. Just to be clear, the information on this website is for meant for educational purposes and you use it at your own risk. I do not take responsibility if you screw something up. Use common sense, do not 'rm -rf /' as root for example. If you have any questions then do not hesitate to contact me. See https://raymii.org/s/static/About.html for details.