Posted in

Ansible Execution Environments in IBM Power Automation.

Anyone who has tried to maintain a consistent environment for running playbooks, whether on a dedicated Ansible Control Node or any other server, knows how quickly reconciling the requirements of different collections, Python versions, and a whole host of dependencies can turn into a nightmare. Fortunately, the answer to this chaos is execution environments. Instead of installing everything on a single host, you enclose the entire automation along with its dependencies in a portable, isolated container, guaranteeing that it will always work the same way everywhere. I want to show how to use this approach in practice to manage the entire IBM Power ecosystem to say goodbye to dependency problems once and for all.

The Old Approach: Ansible Control Node

Imagine a server (a virtual machine) that is your automation command center. On it, you install Ansible, all the necessary collections (ansible-galaxy collection install…), Python libraries (pip install…), and other tools. This is the Ansible Control Node.

Problems with this approach:

  • Chaotic dependency management: On a shared server, anyone can install something “just for themselves,” often breaking something for someone else. Python modules are installed interchangeably by dnf and pip, sometimes globally for root, sometimes in a home directory (–user), and on top of that, there are different versions of Python itself. This leads to complete chaos and difficult-to-diagnose errors.
  • Lack of repeatability and the “but it worked on my machine!” syndrome: The environment on the Control Node has a life of its own. A simple system update (dnf update) can upgrade Ansible or a key library, breaking playbooks that were working just yesterday. Because the development, test, and production servers are never identical, the same code works in one place but not in another.
  • Maintenance nightmare: Who manages this server? What if an operating system update breaks a key Python dependency? The Control Node becomes a shared resource that no one wants to touch for fear that something will stop working.

The New Approach: Execution Environment (EE)

An Execution Environment is a complete, portable, and isolated execution environment, packaged in a container. Instead of installing everything on one server, you define all dependencies in a file, build a container image from it, and run your playbooks inside that container.

Think of it this way: Ansible Control Node is one big, shared workshop where everyone throws their tools into one pile. It’s a mess, and the tools often don’t fit together.Execution Environment is a small, specialized toolbox. For working with IBM Power, you take the “IBM Power toolbox,” for network automation, the “networking toolbox.” Each is perfectly prepared, self-sufficient, and does not conflict with others.

With Execution Environment, automation becomes portable, consistent, and fully reproducible.

Your Own “Toolbox” for IBM Power

For the IBM Power environment, such a “toolbox” (container) will contain everything you need to manage HMC, VIOS, or AIX/IBM i partitions:

  • Ansible Collections: ibm.power_vios, ibm.power_hmc, community.general, etc.
  • Python Libraries: All those that these collections require to communicate with the target systems.
  • System Tools: Maybe sshpass or some specific client if it is needed.

While there are ready-made public EE images, they do not include all collections, especially those for the IBM Power platform, building your own EE also gives you full control, a smaller image size, and greater security.

The build process is simple and comes down to three steps.

Define your needs in the execution-environment.yml file:

# execution-environment.yml
version: 3
dependencies:
    galaxy:
    collections:
        [ ... ]
        - ibm.power_vios
        - ibm.power_hmc
        [ ... ]
    python:
    - requests
    [ ... ]
Bash

Build the container image using ansible-builder: It is good practice to tag the image with both a specific version (1.0) and a latest tag.

ansible-builder build \
    --tag my-company-registry/ibmpower-ee:1.0 \
    --tag my-company-registry/ibmpower-ee:latest
Bash

Push both versions to your company’s repository:

podman push my-company-registry/ibmpower-ee:1.0
podman push my-company-registry/ibmpower-ee:latest
Bash

When building an EE, you can take inspiration from, for example, awx-ee.

Use Scenarios

Now that our dedicated “toolbox” is ready in two variants (:1.0 and :latest), let’s see how to use it.

  • :1.0 (version tag): Use in production environments and wherever stability and repeatability are important. It guarantees that the task will always run with the same, unchanging set of tools.
  • :latest (floating tag): Use in development environments and in CI pipelines that are intended to test code with the latest version of the tools.

Method 1: Use in AWX / Ansible Automation Platform

This is the primary way to use EEs.

  1. Add the EE in AWX: In the administrator panel, go to Administration -> Execution Environments.
  2. Create two entries:
    • For production (stable):
      • Name: IBM Power EE 1.0
      • Image: my-company-registry/ibmpower-ee:1.0
      • Pull: Always pull container image before running
    • For developers (latest):
      • Name: IBM Power EE Latest
      • Image: my-company-registry/ibmpower-ee:latest
      • Pull: Always pull container image before running
  3. Attach the EE to a job template: In the Job Template settings, e.g., “Deploy VIOS Production Config,” in the Execution Environment field, select IBM Power EE 1.0. For development tasks, you can point to IBM Power EE Latest.

Method 2: Integration with GitLab CI (Automatic Deployments)

Instead of installing everything from scratch in every pipeline, you simply point to the ready-made EE image.

# .gitlab-ci.yml
deploy_vios_production:
  stage: deploy
  # For production deployments, we use a specific, frozen version.
  image: 
    name: my-company-registry/ibmpower-ee:1.0

  script:
    - ansible-playbook create_vlan.yml -i inventory.ini --vault-password-file $VAULT_PASS_FILE
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

test_vios_playbook:
  stage: test
  # In tests, we can use :latest to check compatibility with new tools.
  image: 
    name: my-company-registry/ibmpower-ee:latest

  script:
    - ansible-playbook create_vlan.yml --syntax-check
    - ansible-lint create_vlan.yml
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
YAML

Method 3: Working with ansible-navigator

This is a modern way to work with Ansible locally that perfectly simulates how it runs in AWX.

In your project directory, create a file named ansible-navigator.yml:

---
# ansible-navigator.yml
ansible-navigator:
  execution-environment:
    # For daily development work, :latest is convenient
    image: my-company-registry/ibmpower-ee:latest
    pull:
      policy: missing 
YAML

Now, running a playbook is as simple as:

ansible-navigator run create_vlan.yml -i inventory.ini
Bash

If you want to test something on an older version of the EE, you can override the setting with a flag:

ansible-navigator run create_vlan.yml --eei my-company-registry/ibmpower-ee:1.0
Bash

Method 4: Automation with ansible-runner (Advanced Scripts)

ansible-runner is the “under-the-hood” tool that AWX, among others, uses. It is ideal for integrating Ansible with other scripts. It requires a specific directory structure.

A Bash script might look like this:

#!/usr/bin/ksh

# By default, we use the stable version unless the script is called otherwise
EE_IMAGE_TAG=${1:-"1.0"}
EE_IMAGE="my-company-registry/ibmpower-ee:${EE_IMAGE_TAG}"
PRIVATE_DATA_DIR="./power_project/private_data_dir"

echo "Running playbook create_vlan.yml using image: ${EE_IMAGE}"

ansible-runner run "${PRIVATE_DATA_DIR}" \
  -p create_vlan.yml \
  --inventory inventory/hosts \
  --process-isolation \
  --container-image "${EE_IMAGE}"

# Check the result..
LATEST_RUN_DIR=$(ls -td "${PRIVATE_DATA_DIR}/artifacts"/*/ | head -1)
if [ -f "${LATEST_RUN_DIR}/status" ] && [ "$(cat "${LATEST_RUN_DIR}/status")" == "successful" ]; then
    echo "Playbook completed successfully."
    exit 0
else
    echo "Error executing playbook! Check logs in ${LATEST_RUN_DIR}"
    cat "${LATEST_RUN_DIR}/stdout"
    exit 1
fi
Bash

You can now run this script, optionally providing a tag: ./run_playbook.sh latest.

Method 5: Plain podman run (Full Control)

This is great for quick testing and for understanding how it all works under the hood. Run this command from the directory where your playbooks are located.

# Run with the stable version to debug a production issue
podman run --rm -it \
  -v $(pwd):/runner/project:z \
  --workdir /runner/project \
  my-company-registry/ibmpower-ee:1.0 \
  ansible-playbook create_vlan.yml -i inventory.ini
Bash

Let’s break down this command:

  • podman run...: Run a container.
  • -v $(pwd):/runner/project:z: Mount my current directory (pwd) inside the container so it has access to the playbook and inventory.
  • --workdir /runner/project: Set the working directory inside the container.
  • my-company-registry/ibmpower-ee:1.0: Use this specific “toolbox.”
  • ansible-playbook...: And this is the command to be executed inside.

Summary

As you can see, Execution Environments are not just a new feature, but a change in the way of thinking. No more wondering why a playbook works on your machine but not in production. Instead of a mess on a server, you get your own portable toolbox that works the same everywhere. The initial effort to build it pays off instantly in the form of peace of mind, order, and automation that you can finally fully rely on.