This post was originally published on this site
The Oracle provided Ansible module gives us the opportunity to provision and configure Oracle Cloud Infrastructure resources on an automated base. The Ansible basic setup is very easy and the Oracle provided example playbooks in Git are a good base to start with your infrastructure automation project. Oracle provides Ansible example playbooks for
- Block Volumes
- Compute
- Database
- File Storage
- IAM
- Load Balancer
- Private Subnets with VPN
- Delete Objects
- etc.
In this blog post, I will show you how easy it is to bring Ansible and the Oracle Cloud Infrastructure together.
Requirements
- A local machine to install Ansible and the required software and modules, in my case it’s an Oracle Linux 7 virtual machine with Internet access.
- An Oracle Cloud Infrastructure Account with permissons to create new resources.
Steps to configure Ansible and OCI
- Install and configure the Oracle Cloud Infrastructure Python SDK
- Install and configure Ansible
- Download and configure the OCI modules for Ansible
- OCI Test Run
Install and configure the Oracle Cloud Infrastructure Python SDK
In this step, the OCI Python SDK will be installed an configured. The new created SSH public key has to be added in the OCI console for further actions. As OS user root we create a new operating system user called oci for Oracle Cllud Infrastrcuture actions and give him sudo privileges.
Create a User and SSH Keys
# groupadd oci # useradd oci -g oci # passwd oci
Add this line in /etc/sudoers.
oci ALL=(ALL) ALL
Login as user oci, create a new SSH key and download an configure the OCI SDK. Protect your keys.
$ mkdir ~/.oci $ openssl genrsa -out ~/.oci/oci_api_key.pem 2048 $ chmod go-rwx ~/.oci/oci_api_key.pem $ openssl rsa -pubout -in ~/.oci/oci_api_key.pem -out ~/.oci/oci_api_key_public.pem $ chmod go-rwx ~/.oci/oci_api_key_public.pem $ chmod go-rwx ~/.oci/oci_api_key.pem
Show the public key and add it in the OCI console to your cloud account user.
The OCI Configuration File
As user oci, create the Oracle Cloud Infrastructure configuration file
$ vi ~/.oci/config
Content of the file – for example in region Frankfurt and with the created SSH key file from above.
[DEFAULT] user=[OCID of your OCI User] fingerprint=[API Key Fingerprint of added SSH public key] tenancy=[Your tenancy id] region=eu-frankfurt-1 key_file=~/.oci/oci_api_key.pem
Change the file permissions.
$ chmod 600 /home/oci/.oci/config
Install the Oracle Cloud Infrastructure Python SDK
$ sudo yum-config-manager --enable ol7_developer ol7_developer_epel $ sudo yum -y install python-oci-sdk python-oci-cli
Test
Command to list all instances in the selected compartment.
$ oci compute instance list --compartment-id [OCID of your OCI Compartment]| grep display-name "display-name": "Instance-AS-Test-1"
Install and configure Ansible
As user oci, download and install Ansible and Git.
$ sudo yum -y install git ansible $ sudo mkdir -p /usr/share/ansible/modules
Set up the module directory.
$ sudo vi /etc/ansible/ansible.cfg library=/usr/share/ansible/modules
Install additional packages.
$ sudo yum install python-pip $ sudo pip install --upgrade Jinja2
This upgrade step is required, otherwise the public key creation in the OCI Ansible module fails (for example when you want to launch a new Compute instance).
Download and configure the OCI modules for Ansible
As user oci, download the Ansible modules from Git.
$ cd /usr/share/ansible/modules $ sudo git clone https://github.com/oracle/oci-ansible-modules.git
Show the content.
$ ls -la total 4 drwxr-xr-x. 3 root root 33 Mar 11 13:58 . drwxr-xr-x. 3 root root 21 Mar 11 13:56 .. drwxr-xr-x. 12 root root 4096 Mar 11 13:58 oci-ansible-modules
Change into the new created directory and execute the configuration script install.py.
$ cd oci-ansible-modules
$ sudo ./install.py Copying documentation fragments from /usr/share/ansible/modules/oci-ansible-modules/module_docs_fragments to /usr/lib/python2.7/site-packages/ansible/utils/module_docs_fragments Copying oracle utility files from /usr/share/ansible/modules/oci-ansible-modules/module_utils/oracle to /usr/lib/python2.7/site-packages/ansible/module_utils/oracle Creating directory /usr/lib/python2.7/site-packages/ansible/modules/cloud/oracle Copying OCI Ansible modules from /usr/share/ansible/modules/oci-ansible-modules/library to /usr/lib/python2.7/site-packages/ansible/modules/cloud/oracle OCI Ansible modules installed successfully.
OCI Test Run
We copy the example playbook to launch a Compute cloud instance into the local folder and run the playbook. The Oracle provided playbook needs three variables:
SAMPLE_AD_NAME | Availability Domain, e.g. EUZg:EU-FRANKFURT-1-AD-1 |
SAMPLE_IMAGE_OCID | OCID of the selected OS – see https://docs.cloud.oracle.com/iaas/images/ to list all available images |
SAMPLE_COMPARTMENT_OCID | OCID of your compartment – OCI > Identity > Compartments |
Create a working directory and copy the example playbook.
$ sudo mkdir -p ~/ansible/playbooks cd ~/ansible/playbooks $ cp -r /usr/share/ansible/modules/oci-ansible-modules/samples/compute/launch_compute_instance ~/ansible/playbooks $ cd ~/ansible/playbooks/launch_compute_instance
Set variables.
$ export SAMPLE_AD_NAME=EUZg:EU-FRANKFURT-1-AD-1 $ export SAMPLE_IMAGE_OCID=ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq $ export SAMPLE_COMPARTMENT_OCID=ocid1.compartment.oc1..aaaaaaaayc4703470347034703470347034703o3hx2exkz5pzi6kt4kunhiq
Run the playbook
Attention: All OCI resources are created and afterwards terminated immediately. If you don’t want to terminate them, comment out this line in file sample.yaml.
- import_tasks: teardown.yaml
Execute the Ansible playbook. The infrastructure will be created step by step. Key generation, network configuration, firewall rule setup, instance creation etc. is all automated.
$ ansible-playbook sample.yaml [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all' PLAY [Launch a compute instance and connect to it using SSH] ******************************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************************************************************************* ok: [localhost] TASK [Check pre-requisites] **************************************************************************************************************************************************** skipping: [localhost] => (item=SAMPLE_COMPARTMENT_OCID) skipping: [localhost] => (item=SAMPLE_IMAGE_OCID) skipping: [localhost] => (item=SAMPLE_AD_NAME) TASK [Create a temporary directory to house a temporary SSH keypair we will later use to connect to instance] ****************************************************************** changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Generate a Private Key] ************************************************************************************************************************************************** changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Generate a Public Key] *************************************************************************************************************************************************** changed: [localhost] TASK [Create a VCN] ************************************************************************************************************************************************************ changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Create a new Internet Gateway] ******************************************************************************************************************************************* changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Create route table to connect internet gateway to the VCN] *************************************************************************************************************** changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [create ingress rules yaml body] ****************************************************************************************************************************************** ok: [localhost] TASK [create egress yaml body] ************************************************************************************************************************************************* ok: [localhost] TASK [load the variables defined in the ingress rules yaml body] *************************************************************************************************************** ok: [localhost] TASK [print loaded_ingress] **************************************************************************************************************************************************** ok: [localhost] => { "msg": "loaded ingress is {u'instance_ingress_security_rules': [{u'source': u'0.0.0.0/0', u'protocol': u'6', u'tcp_options': {u'destination_port_range': {u'max': 22, u'min': 22}}}]}" } TASK [load the variables defined in the egress rules yaml body] **************************************************************************************************************** ok: [localhost] TASK [print loaded_egress] ***************************************************************************************************************************************************** ok: [localhost] => { "msg": "loaded egress is {u'instance_egress_security_rules': [{u'tcp_options': {u'destination_port_range': {u'max': 22, u'min': 22}}, u'destination': u'0.0.0.0/0', u'protocol': u'6'}]}" } TASK [Create a security list for allowing access to public instance] *********************************************************************************************************** changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Create a subnet to host the public instance. Link security_list and route_table.] **************************************************************************************** changed: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Launch an instance] ****************************************************************************************************************************************************** changed: [localhost] TASK [Print instance details] ************************************************************************************************************************************************** ok: [localhost] => { "msg": "Launched a new instance {u'added_instances': [{u'lifecycle_state': u'RUNNING', u'availability_domain': u'EUZg:EU-FRANKFURT-1-AD-1', u'display_name': u'my_test_instance', u'time_maintenance_reboot_due': None, u'compartment_id': u'ocid1.compartment.oc1..aaaaaaaayc5kgqshdb5g2mjg4bnt34htnybbho3hx2exkz5pzi6kt4kunhiq', u'defined_tags': {}, u'region': u'eu-frankfurt-1', u'freeform_tags': {}, u'time_created': u'2019-03-11T13:52:34.238000+00:00', u'launch_options': {u'remote_data_volume_type': u'PARAVIRTUALIZED', u'firmware': u'UEFI_64', u'boot_volume_type': u'PARAVIRTUALIZED', u'is_consistent_volume_naming_enabled': True, u'network_type': u'VFIO', u'is_pv_encryption_in_transit_enabled': True}, u'image_id': u'ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq', u'source_details': {u'source_type': u'image', u'kms_key_id': None, u'boot_volume_size_in_gbs': None, u'image_id': u'ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq'}, u'fault_domain': u'FAULT-DOMAIN-3', u'shape': u'VM.Standard1.1', u'launch_mode': u'NATIVE', u'agent_config': {u'is_monitoring_disabled': False}, u'extended_metadata': {}, u'ipxe_script': None, u'id': u'ocid1.instance.oc1.eu-frankfurt-1.abtheljslxpqenvnafkstq2s7hxqizyam7shjd6kihyg33i7w2geeynedotq', u'metadata': {u'ssh_authorized_keys': u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAA4703470347034703470347034703470347034703470347034703470347034703XR6Zcv4sHKdMH/aDfZKFaLpqHtJ2P+JEC0ok2lWrPQSG0zPoSYWTrldllpgSfzOv1kQ1R/Uor+eedRz9x2DXtKl0anSXY6KNW1GVfOvoFk/c8AuaQy6Y7AZnJAculyF1pRJiHQyZEPuBL9E9EsiRqkQR18G9yewBApRZf/QGXZbLydG8vAa9T1DCYrODb3N2u73IIqbMFwr7sgQ8XQb7h9wlYq2mfUKxy+8H4w7S67'}}], u'instances': [{u'lifecycle_state': u'RUNNING', u'availability_domain': u'EUZg:EU-FRANKFURT-1-AD-1', u'display_name': u'my_test_instance', u'time_maintenance_reboot_due': None, u'compartment_id': u'ocid1.compartment.oc1..aaaaaaaayc5kgqshdb5g2mjg4bnt34htnybbho3hx2exkz5pzi6kt4kunhiq', u'defined_tags': {}, u'region': u'eu-frankfurt-1', u'freeform_tags': {}, u'time_created': u'2019-03-11T13:52:34.238000+00:00', u'launch_options': {u'remote_data_volume_type': u'PARAVIRTUALIZED', u'firmware': u'UEFI_64', u'boot_volume_type': u'PARAVIRTUALIZED', u'is_consistent_volume_naming_enabled': True, u'network_type': u'VFIO', u'is_pv_encryption_in_transit_enabled': True}, u'image_id': u'ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq', u'source_details': {u'source_type': u'image', u'kms_key_id': None, u'boot_volume_size_in_gbs': None, u'image_id': u'ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq'}, u'fault_domain': u'FAULT-DOMAIN-3', u'shape': u'VM.Standard1.1', u'launch_mode': u'NATIVE', u'agent_config': {u'is_monitoring_disabled': False}, u'extended_metadata': {}, u'ipxe_script': None, u'id': u'ocid1.instance.oc1.eu-frankfurt-1.abtheljslxpqenvnafkstq2s7hxqizyam7shjd6kihyg33i7w2geeynedotq', u'metadata': {u'ssh_authorized_keys': u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAA4703470347034703470347034703470347034703470347034703470347034703XR6Zcv4sHKdMH/aDfZKFaLpqHtJ2P+JEC0ok2lWrPQSG0zPoSYWTrldllpgSfzOv1kQ1R/Uor+eedRz9x2DXtKl0anSXY6KNW1GVfOvoFk/c8AuaQy6Y7AZnJAculyF1pRJiHQyZEPuBL9E9EsiRqkQR18G9yewBApRZf/QGXZbLydG8vAa9T1DCYrODb3N2u73IIqbMFwr7sgQ8XQb7h9wlYq2mfUKxy+8H4w7S67'}}], u'changed': True, 'failed': False, u'instance': {u'lifecycle_state': u'RUNNING', u'fault_domain': u'FAULT-DOMAIN-3', u'extended_metadata': {}, u'time_maintenance_reboot_due': None, u'compartment_id': u'ocid1.compartment.oc1..aaaaaaaayc5kgqshdb5g2mjg4bnt34htnybbho3hx2exkz5pzi6kt4kunhiq', u'defined_tags': {}, u'region': u'eu-frankfurt-1', u'freeform_tags': {}, u'time_created': u'2019-03-11T13:52:34.238000+00:00', u'source_details': {u'source_type': u'image', u'image_id': u'ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq', u'kms_key_id': None, u'boot_volume_size_in_gbs': None}, u'agent_config': {u'is_monitoring_disabled': False}, u'image_id': u'ocid1.image.oc1.eu-frankfurt-1.aaaaaaaan7ghs3nhu6bbujqnyukj755642xnmzshck5pm5svol6uigkxl2hq', u'shape': u'VM.Standard1.1', u'availability_domain': u'EUZg:EU-FRANKFURT-1-AD-1', u'launch_mode': u'NATIVE', u'ipxe_script': None, u'display_name': u'my_test_instance', u'launch_options': {u'remote_data_volume_type': u'PARAVIRTUALIZED', u'firmware': u'UEFI_64', u'boot_volume_type': u'PARAVIRTUALIZED', u'is_consistent_volume_naming_enabled': True, u'network_type': u'VFIO', u'is_pv_encryption_in_transit_enabled': True}, u'id': u'ocid1.instance.oc1.eu-frankfurt-1.abtheljslxpqenvnafkstq2s7hxqizyam7shjd6kihyg33i7w2geeynedotq', u'metadata': {u'ssh_authorized_keys': u'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAA4703470347034703470347034703470347034703470347034703470347034703XR6Zcv4sHKdMH/aDfZKFaLpqHtJ2P+JEC0ok2lWrPQSG0zPoSYWTrldllpgSfzOv1kQ1R/Uor+eedRz9x2DXtKl0anSXY6KNW1GVfOvoFk/c8AuaQy6Y7AZnJAculyF1pRJiHQyZEPuBL9E9EsiRqkQR18G9yewBApRZf/QGXZbLydG8vAa9T1DCYrODb3N2u73IIqbMFwr7sgQ8XQb7h9wlYq2mfUKxy+8H4w7S67'}}}" } TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Get the VNIC attachment details of instance] ***************************************************************************************************************************** ok: [localhost] TASK [Get details of the VNIC] ************************************************************************************************************************************************* ok: [localhost] TASK [set_fact] **************************************************************************************************************************************************************** ok: [localhost] TASK [Print the public ip of the newly launched instance] ********************************************************************************************************************** ok: [localhost] => { "msg": "Public IP of launched instance 130.61.40.231" } TASK [Wait (upto 5 minutes) for port 22 to become open] ************************************************************************************************************************ ok: [localhost] TASK [Attempt a ssh connection to the newly launced instance] ****************************************************************************************************************** changed: [localhost] TASK [Print SSH response from launched instance] ******************************************************************************************************************************* ok: [localhost] => { "msg": "SSH response from instance -> [u'Linux mytestinstance 3.10.0-957.5.1.el7.x86_64 #1 SMP Fri Feb 1 14:54:57 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux']" } TASK [Terminate the instance] ************************************************************************************************************************************************** changed: [localhost] TASK [Delete the subnet] ******************************************************************************************************************************************************* changed: [localhost] TASK [Delete the security list] ************************************************************************************************************************************************ changed: [localhost] TASK [Delete the route table] ************************************************************************************************************************************************** changed: [localhost] TASK [Delete the Internet Gateway] ********************************************************************************************************************************************* changed: [localhost] TASK [Delete the VCN] ********************************************************************************************************************************************************** changed: [localhost] PLAY RECAP ********************************************************************************************************************************************************************* localhost : ok=38 changed=16 unreachable=0 failed=0
Ready to Use
After a few minutes, a complete infrastructure for an OCI compute instance is created and the instance is ready to connect.
The required SSH keys for the terminal connection were generated in a subdirectory of /tmp with the prefix ansible. In my example, the private and the public SSH key are located in /tmp/ansible.v6ckX0cert.
$ pwd /tmp/ansible.v6ckX0cert $ ll total 8 -rw-------. 1 oci oci 1708 Mar 11 14:03 private_key.pem -rw-rw-r--. 1 oci oci 380 Mar 11 14:03 public_key.pem
Links
- https://docs.cloud.oracle.com/iaas/Content/API/SDKDocs/ansiblegetstarted.htm
- https://blogs.oracle.com/linux/installing-python-sdk-and-cli-for-oracle-cloud-infrastructure-on-oracle-linux:-a-tutorial
- https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html
- https://blogs.oracle.com/cloud-infrastructure/announcing-oracle-cloud-infrastructure-ansible-modules
- https://github.com/oracle/oci-ansible-modules/tree/master/samples
Summary
The Oracle provided Ansible playbooks are a good entry point to start with OCI automation. I am already working at the next tasks to make my work easier with more variables and simplified playbooks. And finally I want to integrate it in Ansible AWX. Well done Oracle!