Crossplane Ansible Provider: Getting Started

2022_11_Blog_OG_Ansible-Provider_1200x630-1
date icon

October 21, 2022

author icon

Fahed Dorgaa

read time icon

Reading time: 7 min read

Share:

LinkedIn icon
Twitter icon
Facebook icon

Today we are very excited to announce a new open source project, provider-ansible, a Crossplane provider designed to extend the Crossplane scope by enabling its integration with Ansible.

Why Ansible Crossplane Provider

Ansible is a popular automation technology. It is widely adopted by many IT organizations nowadays to automate management for their IT systems, ranging from non cloud native to cloud native. It has a very mature community and an ecosystem with many existing automation assets that are publicly available at https://galaxy.ansible.com/. In the meantime, many IT organizations have their own private Ansible assets accumulated for years to address their specific IT management requirements.

On the other hand, Crossplane, as a cloud native control plane framework, has evolved rapidly in recent years and now has become a very active CNCF project. It allows people to drive IT automation in a cloud native manner and orchestrate management automation for different varieties of IT resources using its powerful composition engine. All these can happen inside a Kubernetes cluster.

To combine the two powerful tools together and unleash the synergy, we have built a Crossplane provider for Ansible. It works as a bridge that can connect the two communities.

If you are familiar with Ansible and want to get benefits from what Crossplane provides, the Crossplane provider for Ansible is definitely a good option for you! For example, you can use this provider to drive Ansible automation inside a Kubernetes cluster rather than from the command line. By doing so, your Ansible automation can work with some modern IT operation methodology such as GitOps seamlessly. You can also use this provider to orchestrate multiple resource management automation approaches including Ansible using Crossplane’s composition mechanism. This will allow you to mix the use of many great automation assets from both two communities consistently.

This provider also opens a door to IT organizations who may have already invested lots of money on their IT systems management automation by using Ansible, including those traditional systems that do not behave cloud-natively, and want to adopt Crossplane without losing their investments. They can now reuse their existing automation assets without reinventing the wheels, specifically in some case when there’s no easy replacement for their traditional automation assets in Crossplane community in cloud native world. For example, to provision a VM in a LAN and install something on that is easy to be done by Ansible, but in Crossplane, it may need to introduce a new provider which is not necessary especially when people have some Ansible assets in place already.

In the next few sections, you will learn how the Crossplane provider for Ansible works at an architectural level in order to enable the above use cases. You will also learn how to drive Ansible execution using this provider by going through a couple of concrete examples step by step. Keep reading!

Understanding How It Works

The Ansible provider execution is driven by the Crossplane ecosystem.

Fig.1 Ansible provider Architecture

In Fig.1, we see the high level architecture for the Ansible provider: it uses Kubernetes resource AnsibleRun to leverage Ansible content, e.g.: Playbook, Role, etc., to do the actual work. The AnsibleRun resource refers to the ProviderConfig resource which includes the required information for the proper functioning of our Ansible provider, e.g.: credentials, requirements, behavior variables, etc.

Within the Crossplane reconciliation process, the Ansible provider relies on standard Ansible binaries to execute the job: it uses the ansible-galaxy cli to download Ansible contents specified in the AnsibleRun resource using credentials from ProviderConfig resource, then depending on different run policy specified, it will invoke ansible-runner cli to execute them at different time.

The Hello World Example

In this section, let’s start with a very simple example to explore the basic use of Ansible provider. Our first example is the hello world Ansible playbook which will print a “Hello world!” message to the console.

To demonstrate the use of Ansible provider, let’s start a KIND cluster at first. KIND is a local Kubernetes cluster using Docker container “nodes”. To start the cluster, you need KIND CLI. For more information on KIND, please refer to https://kind.sigs.k8s.io/.

Run below command to start the cluster:

kind create cluster --image kindest/node:v1.23.0 --wait 5m --name provider-ansible-demo

Then, install Crossplane into the cluster:

kubectl create namespace crossplane-system
helm repo add crossplane-stable <https://charts.crossplane.io/stable>
helm repo update
helm install crossplane --namespace crossplane-system crossplane-stable/crossplane

And the Crossplane CLI, which is essentially a kubectl plugin that extends kubectl with functionality to build, push, and install Crossplane packages:

curl -sL <https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh> | sh

Then, run below command to install the Ansible provider:

kubectl crossplane install provider crossplanecontrib/provider-ansible:v0.3.0

After the provider is installed, you can run below command to verify if the installation is successful:

kubectl get crossplane

If everything works fine, you should see that both the column INSTALLED and HEALTHY for the Provider resource are True. For example:

NAME                                                                                 HEALTHY   REVISION   IMAGE                                       STATE    DEP-FOUND   DEP-INSTALLED   AGE

providerrevision.pkg.crossplane.io/crossplanecontrib-provider-ansible-6fb5112f17a6   True      1          crossplanecontrib/provider-ansible:v0.3.0   Active                               33m


NAME                                                            INSTALLED   HEALTHY   PACKAGE                                     AGE

provider.pkg.crossplane.io/crossplanecontrib-provider-ansible   True        True      crossplanecontrib/provider-ansible:v0.3.0   33m


NAME                                        AGE   TYPE         DEFAULT-SCOPE

storeconfig.secrets.crossplane.io/default   48d   Kubernetes   crossplane-system

After then, apply the following YAML manifests which include a ProviderConfig and an AnsibleRun:

---

apiVersion: ansible.crossplane.io/v1alpha1

kind: ProviderConfig

metadata:

  name: default

spec:

  credentials: []

---

apiVersion: ansible.crossplane.io/v1alpha1

kind: AnsibleRun

metadata:

  name: example

spec:

  forProvider:

    playbookInline: |

      ---

      - hosts: localhost

        tasks:

          - name: ansibleplaybook-simple

            debug:

              msg: Hello world!

In this example, we do not really need any real credential to access a remote repository that hosts Ansible roles or playbooks, so the spec.credentials field in ProviderConfig is empty. For AnsibleRun, we define the hello world playbook as an inline playbook by spec.forProvider.playbookInline. After applying these YAML manifests, you will see something similar to the following in the Ansible provider pod logs:

[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'
PLAY [localhost] ***************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
TASK [ansibleplaybook-simple] **************************************************
ok: [localhost] => {
"msg": "Hello world!"
}
PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

As you can see, the hello world message was printed out to the console. This is exactly the same as you see when running Ansible playbook from command line, the only difference is that you can now drive the Ansible run in a cloud native way by applying a Kubernetes custom resource and customizing the run by modifying the custom resource at runtime.

A More Powerful Example

In this section, we are introducing a more complex custom resource. Our example target is to create and maintain a GCP bucket.

First of all we need to create a Kubernetes secret that handles the provider credentials, GCP in this case.

apiVersion: v1

kind: Secret

metadata:

 namespace: crossplane-system

 name: gcp-credentials

type: Opaque

data:

 credentials: BASE64ENCODED_PROVIDER_CREDS

Then we should introduce this secret in the ProviderConfig resource which is used to authenticate against upstream API and repositories:

apiVersion: ansible.crossplane.io/v1alpha1 

kind: ProviderConfig 

metadata: 

  name: gcpconfig 

spec: 

 # Note that unlike most provider configs this one supports an array of 

 # credentials. This is because     each Ansible playbook uses a single 

 # Crossplane provider config, but could use multiple providers  each 

 # with their own credentials.

   credentials: 

     - filename: gcp-credentials.json 

       source: Secret 

       secretRef: 

         namespace: crossplane-system 

         name: gcp-credentials 

         key: credentials


Once we create ProviderConfig ressource, the last step is always to create the AnsibleRun resource:

apiVersion: ansible.crossplane.io/v1alpha1

kind: AnsibleRun

metadata:

 annotations:

   ansible.crossplane.io/runPolicy: CheckWhenObserve

 name: gcpdisk

spec:

 forProvider:

   # AnsibleRun default to using a remote source

   # For simple cases you can use an inline source to specify the content of

   # playbook.yaml as opaque, inline yaml.

  roles:

     - name: ansible_provider.gcpdisk_role

       src: https://github.com/multicloudlab/crossplane-ansible-provider-sample.git

   vars:

     project:

       disk:

         size: 20

         key: 718BDCC469891

         zone: europe-west1-b

       id: test_project

 providerConfigRef:

   name: gcpconfig


In the above AnsibleRun resource, we are using a remote Ansible role hosted by a remote git repository at: https://github.com/multicloudlab/crossplane-ansible-provider-sample.git, and relying on the gcpconfig as ProviderConfig.

By checking the above AnsibleRun manifest, you may also notice that it allows us to customize the run by passing variables using spec.forProvider.vars. As you know, Ansible uses variables to manage differences among systems on which Ansible operates, so it can run roles or playbooks on multiple systems by using a single command. Ansible provider allows you to pass those differences into Ansible run when you define the AnsibleRun resource, in this case, to customize the gcp bucket provisioning, e.g.: specify the disk size, key, and zone, etc.

Find the Ansible Provider in the Upbound Marketplace

Previously, when Crossplane contributors or third-party organizations wanted to introduce new providers to the community, it took a lot of effort to ensure that users were aware of its existence and knew how to find it. With the recent launch of the Upbound Marketplace, new providers like provider-ansible can be easily discovered by anyone searching for keywords, such as “ansible”. Furthermore, the Marketplace allows package authors the ability to include documentation and examples for any resource, such as AnsibleRun, with minimal effort.

This ease of use empowers platform teams not only to start using the Ansible provider directly, but also to build and publish their own abstractions in the form of configuration packages built on its primitives.

Conclusion

Congratulations! You have worked through all the steps needed to run an Ansible playbook or role, either inline or hosted remotely, and customize the run by defining variables. It all happens inside a Kubernetes cluster, and driven by the Crossplane Ansible provider.

If you are interested in this project and want to join with us, feel free to open issues or submit pull requests to: https://github.com/crossplane-contrib/provider-ansible.

Subscribe to the Upbound Newsletter