Introduction

In today’s IT landscape, the automation of cloud infrastructure has become a pivotal aspect of managing scalable, reliable, and efficient systems. Oracle Cloud Infrastructure (OCI) is one such platform that benefits greatly from automation. This article will guide you through creating a compute instance pool and launching instances in OCI using Ansible, providing a detailed example and best practices.

Understanding Ansible

Ansible is an open-source automation tool that simplifies the management of complex IT environments. It uses a human-readable language, YAML, to describe automation jobs, known as playbooks. Ansible is agentless, using SSH or WinRM for communication, making it a secure and efficient choice for IT automation.

Key Features of Ansible

  1. Human-Readable Automation: Uses YAML for playbooks, making it easy to read and write.
  2. Agentless Architecture: Requires no agents on the managed nodes, reducing overhead and security risks.
  3. Cross-Platform Support: Works with Linux, Windows, UNIX, and network devices.
  4. Extensible: Can be extended with modules and plugins written in any language.

Setting Up the Environment

Before diving into the playbook, ensure you have the following prerequisites:

  1. Ansible Installed: Ensure Ansible is installed on your control node. You can install it using pip:
    pip install ansible
    
  2. OCI CLI Configured: Configure the OCI CLI on your control node. Follow the OCI CLI installation guide.

Sample Playbook: Creating a Compute Instance Pool

Below is a sample Ansible playbook to create a compute instance pool in OCI and launch instances.

---
# Copyright (c) 2020, 2024 Oracle and/or its affiliates.
# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Apache License v2.0
# See LICENSE.TXT for details.

- name: Create a compute instance pool and launch instances
  hosts: localhost
  collections:
    - oracle.oci
  vars:
    # Common networking definitions
    quad_zero_route: "0.0.0.0/0"
    TCP_protocol: "6"
    SSH_port: "22"

    vcn_name: "ansible-sample-create-instance-pool-vcn"
    vcn_cidr_block: "10.0.0.0/16"
    vcn_dns_label: "samplevcn"

    ig_name: "ansible-sample-create-instance-pool-ig"

    route_table_name: "ansible-sample-create-instance-pool-rt"
    # Route all internet access to our Internet Gateway
    route_table_rules:
      - cidr_block: "{{ quad_zero_route }}"
        network_entity_id: "{{ ig_id }}"

    subnet_cidr: "10.0.0.48/28"
    subnet_name: "ansible-sample-create-instance-pool-subnet"
    subnet_dns_label: "samplesubnet"

    securitylist_name: "ansible-sample-create-instance-pool-sec-list"

    instance_shape: "{{ lookup('env', 'SAMPLE_INSTANCE_SHAPE') | default('VM.Standard2.1', true) }}"
    instance_display_name: "ansible-sample-create-instance-pool-instance"
    instance_pool_configuration_name: "ansible-sample-create-instance-pool-configuration"
    instance_pool_name: "ansible-sample-create-instance-pool"

    #########################################
    # Tenancy specific configuration
    # *Note* - Override the following variables based on your tenancy
    # or set a valid value for the corresponding environment variable
    #########################################
    instance_ad: "{{ lookup('env', 'SAMPLE_AD_NAME') }}"
    instance_compartment: "{{ lookup('env', 'SAMPLE_COMPARTMENT_OCID') }}"
    # Provide an "OL" image
    instance_image: "{{ lookup('env', 'SAMPLE_IMAGE_OCID') }}"

  tasks:
    - block:
        - import_tasks: setup.yaml

        #==========================================================================================
        - name: Create a new basic compute instance configuration that has launch_details and VNIC configuration
          oci_compute_management_instance_configuration:
            compartment_id: "{{instance_compartment}}"
            name: "{{ instance_pool_configuration_name }}"
            instance_details:
              instance_type: "compute"
              launch_details:
                compartment_id: "{{instance_compartment}}"
                create_vnic_details:
                  assign_public_ip: True
                  display_name: "{{instance_display_name}}"
                display_name: "{{instance_display_name}}"
                shape: "{{instance_shape}}"
                source_details:
                  source_type: "image"
                  image_id: "{{instance_image}}"
                metadata:
                  ssh_authorized_keys: "{{ lookup('file',  my_test_public_key ) }}"
          register: result

        - name: Print instance configuration details
          debug:
            msg: "Created a new instance configuration {{ result.instance_configuration }}"

        - set_fact:
            instance_configuration_id: "{{result.instance_configuration.id }}"

        #==========================================================================================
        - name: Create a new instance pool from this instance configuration
          oci_compute_management_instance_pool:
            name: "{{ instance_pool_name }}"
            compartment_id: "{{instance_compartment}}"
            instance_configuration_id: "{{instance_configuration_id}}"
            # Launch 2 instances when pool is created
            size: 2
            placement_configurations:
              - availability_domain: "{{instance_ad}}"
                primary_subnet_id: "{{instance_subnet_id}}"
          register: result

        - set_fact:
            instance_pool_id: "{{result.instance_pool.id}}"

        - name: Print instance pool details
          debug:
            msg: "Created a new instance pool {{ result.instance_pool }}"

        #==========================================================================================
        - name: Get instance details of instances in the instance pool
          oci_compute_management_instance_pool_instance_facts:
            compartment_id: "{{instance_compartment}}"
            id: "{{instance_pool_id}}"
          register: result

        - name: Print details of instances in the new instance pool
          debug:
            msg: "Instances in instance pool {{instance_pool_id}} are {{ result.instance_pool_instances }}"

        - name: Get the first instance from the pool
          set_fact:
            instance_id: "{{result.instance_pool_instances[0].id}}"

        #==========================================================================================
        - name: Get the VNIC attachment details of that instance
          oci_compute_vnic_attachment_facts:
            compartment_id: "{{ instance_compartment }}"
            instance_id: "{{ instance_id }}"
          register: result

        - name: Get details of the VNIC of that VNIC attachment
          oci_network_vnic_facts:
            id: "{{ result.vnic_attachments[0].vnic_id }}"
          register: result

        - set_fact:
            instance_public_ip: "{{result.vnic.public_ip}}"

        - name: Print the public ip of the newly launched instance
          debug:
            msg: "Public IP of launched instance {{ instance_public_ip }}"

        #==========================================================================================
        - name: Wait (upto 10 minutes) for port 22 to become open
          wait_for:
            port: 22
            host: '{{ instance_public_ip }}'
            state: started
            delay: 10
            timeout: 600
          vars:
            ansible_connection: local

        #==========================================================================================
        - name: Attempt a ssh connection to the newly launched instance
          # Use "opc" user as this is an OL image
          # Disable SSH's strict host key checking just for this one command invocation
          command: ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -i {{ temp_certificates_path }}/private_key.pem opc@{{ instance_public_ip }} uname -a
          retries: 3
          delay: 5
          register: result
          until: result.rc == 0

        - name: Print SSH response from launched instance
          debug:
            msg: "SSH response from instance -> {{ result.stdout_lines }}"

        #==========================================================================================
        - import_tasks: teardown.yaml

      rescue:
        - import_tasks: teardown.yaml
          ignore_errors: yes

        - fail:
            msg: "{{ ansible_failed_result }}"

Code from https://github.com/oracle/oci-ansible-collection/tree/master/samples/compute/create_instance_pool

Explanation of the Playbook

  • Setup: This section imports the setup.yaml task file to initialize the environment.
  • Instance Configuration: It creates a new instance configuration with details about the instance type, shape, and VNIC settings.
  • Instance Pool Creation: It creates an instance pool using the previously defined configuration and launches two instances.
  • VNIC Details: Retrieves and prints the VNIC attachment details of the instances.
  • SSH and Validation: Waits for SSH port 22 to become available and attempts an SSH connection to validate the instance is running correctly.
  • Teardown: This section imports the teardown.yaml task file to clean up resources if needed.

Best Practices for Ansible with OCI

  1. Modular Playbooks: Break down your playbooks into smaller, reusable tasks using import_tasks.
  2. Environment Variables: Use environment variables for sensitive information to avoid hardcoding them in playbooks.
  3. Error Handling: Use Ansible’s error handling mechanisms (block, rescue, ignore_errors) to manage failures gracefully.
  4. Testing: Regularly test your playbooks in a staging environment before applying them in production.

Conclusion

Automating OCI with Ansible can significantly streamline your cloud operations, reduce human error, and enhance overall efficiency. By following the example playbook and best practices outlined in this article, you can create and manage compute instance pools effectively. Embrace automation to stay ahead in the dynamic world of cloud computing.

For further reading and advanced topics, consider exploring additional resources and official documentation provided by Oracle and the Ansible community.

Subscribe to the YouTube channel, Medium, and Website, X (formerly Twitter) to not miss the next episode of the Ansible Pilot.

Academy

Learn the Ansible automation technology with some real-life examples in my Udemy 300+ Lessons Video Course.

BUY the Complete Udemy 300+ Lessons Video Course

My book Ansible By Examples: 200+ Automation Examples For Linux and Windows System Administrator and DevOps

BUY the Complete PDF BOOK to easily Copy and Paste the 250+ Ansible code

Want to keep this project going? Please donate

Patreon Buy me a Pizza