What is an Ansible Execution Environnement, by the way ?

As per RedHat definition an Ansible Execution Environment (EE) is a standard way to build and distribute the environment that automation runs in, which reduces complexity and makes it faster and simpler to develop and deploy automation.

More simple, an EE is a container, by default [Podman], which contains everything needed to execute ansible jobs and playbooks, including python, ansible-core, libraries and dependencies, and all stuff that you want add. And of course, It can be distributed as any container.

By default when you install Ansible Automation Platform these execution environments are installed by default, but you can build your owns.

Here what you got by default:

Short NameContainer imageDescription
Minimalee-minimal-rhel8A minimal automation execution environment based on Ansible Core 2.11. only includes the ansible.builtin
Supportedee-supported-rhel8An automation execution environment based on Ansible Core 2.11 that includes Red Hat Ansible Certified Content Collections and their software dependencies.
Compatibilityee-2rhel9An automation execution environment based on Ansible 2.9.

All certified container images are here

The aim of this post is to build an EE from scratch.

The VM configuration

I used a new test VM, with a fresh Ubuntu installation

[ubuntu:~]$  grep DISTRIB_DESCRIPTION /etc/lsb-release                                                                                                                                  

[ubuntu:~]$ sudo apt-get install podman

Install Ansible Navigator

[ubuntu:~]$ python3 -m pip install ansible-navigator --user

[ubuntu:~]$ python3 -m pip install ansible-builder --user

[ubuntu:~]$ python3 -m pip install ansible-runner --user

[ubuntu:~]$ echo 'export PATH=$HOME/.local/bin:$PATH' >> ~/.profile

[ubuntu:~]$ source ~/.profile

[ubuntu:~]$ ansible-navigator
Execution environment image and pull policy overview
Execution environment image name:
Execution environment image tag:      v0.9.1
Execution environment pull arguments: None
Execution environment pull policy:    tag
Execution environment pull needed:    True
Updating the execution environment
Running the command: podman pull
Trying to pull
Getting image source signatures
Copying blob fee18ea417d6 [======================================] 47.2MiB / 47.2MiB

At first execution ansible-navigator pull the creator-ee image.

Prepare the build process

Create a working directory to prepare the files that you need to build the EE container image. The ansible-builder command searches in this working directory for its execution-environment.yml configuration file, which is used to build the container image.

[ubuntu:~]$ mkdir my-builder-env

[ubuntu:~]$ cd my-builder-env


[ubuntu:~/my-builder-env]$ cat execution-environment.yml

version: 1


ansible_config: ansible.cfg 
  galaxy: requirements.yml 
  python: requirements.txt
  system: bindep.txt
		- ADD my_tuf /usr/share/my/stuff
		- COPY .... 
  • EE_BASE_IMAGE : this is the container image to use as starting point. For public access use
  • EE_BUILDER_IMAGE: selects the container to use to build the final image. This is optional.
  • The dependencies sections list all dependencies to be added in the builded container:
    • requirements.yml for additional ansible collections.
    • requirements.txt for additional python modules.
    • bindeb.txt for additional packages.
  • additional_build_steps list all optional steps to be executed at the container creation. These are usual commands used in container creation process.
  • The ansible.cfg, requirements.yml, requirements.txt and bindeb.txt files content:
[ubuntu:~/my-builder-env]$ cat ansible.cfg

server_list = galaxy


# Add and community.general to the EE container
[ubuntu:~/my-builder-env]$ cat requirements.yml
  - community.general

# Add json and requests to the EE container.
[ubuntu:~/my-builder-env]$ cat requirements.txt

# Add rsync and git to the EE container.
[ubuntu:~/my-builder-env]$ cat bindep.txt

Build the container image

Note: To use Red Hat container registry that hosts the default images, ee-minimal-rhel8 and ansible-builder-rhel8 a Redhat subscription is needed (see EE_BASE_IMAGE field)

For public access use instead.

[ubuntu:~/my-builder-env]$ podman login
Username: [email protected]
Login Succeeded!

[ubuntu:~/my-builder-env]$ ansible-builder build --tag ee-test:v1.0
Running command:
  podman build -f context/Containerfile -t ee-test:v1.0 context
Complete! The build context can be found at: /home/ubuntu/my-builder-env/context

[ubuntu:~/my-builder-env]$ cd context
[ubuntu:~/my-builder-env/context]$ tree
|-- Containerfile
`-- _build
    |-- ansible.cfg
    |-- bindep.txt
    |-- requirements.txt
    `-- requirements.yml

The execution environment builder operates in two steps which can be executed separately:

  1. The command creates the context/ directory in the current directory. In that directory, it creates the Containerfile file, the equivalent of a Dockerfile file, which contains instructions for the podman build command. It also creates an _build/ subdirectory and then copies your requirements.ymlansible.cfgrequirements.txt, and bindep.txt files into it, so that the build process can access them.
  2. The command then runs the podman build command, which constructs the resulting automation execution environment container image.

To execute only the first step use the command ansible-builder create command:

[ubuntu:~ ]$ cd my-builder-env
[ubuntu:~/my-builder-env]$ ls
ansible.cfg bindep.txt execution-environment.yml requirements.txt requirements.yml

[ubuntu:~/my-builder-env]$ ansible-builder create
Complete! The build context can be found at: ubuntu:~/my-builder-env/context]

[ubuntu:~/my-builder-env]$ tree
├── ansible.cfg
├── bindep.txt
├── context
│   ├── _build
│   │   ├── ansible.cfg
│   │   ├── bindep.txt
│   │   ├── requirements.txt
│   │   └── requirements.yml
│   └── Containerfile
├── execution-environment.yml
├── requirements.txt
└── requirements.yml

The file Containerfile can be edited to add extra stuff:

[ubuntu:~/my-builder-env]$ cd context

[ubuntu:~/my-builder-env/context ]$ cat Containerfile


USER root

ADD _build/ansible.cfg ~/.ansible.cfg

ADD _build /build
WORKDIR /build

RUN ansible-galaxy role install $ANSIBLE_GALAXY_CLI_ROLE_OPTS -r requirements.yml --roles-path "/usr/share/ansible/roles"
RUN ANSIBLE_GALAXY_DISABLE_GPG_VERIFY=1 ansible-galaxy collection install $ANSIBLE_GALAXY_CLI_COLLECTION_OPTS -r requirements.yml --collections-path "/usr/share/ansible/collections"


COPY --from=galaxy /usr/share/ansible /usr/share/ansible

ADD _build/requirements.txt requirements.txt
ADD _build/bindep.txt bindep.txt
RUN ansible-builder introspect --sanitize --user-pip=requirements.txt --user-bindep=bindep.txt --write-bindep=/tmp/src/bindep.txt --write-pip=/tmp/src/requirements.txt
RUN assemble

USER root

COPY --from=galaxy /usr/share/ansible /usr/share/ansible

COPY --from=builder /output/ /output/
RUN /output/install-from-bindep && rm -rf /output/wheels
LABEL ansible-execution-environment=true

To execute the build step:

[ubuntu:~/my-builder-env]$ podman build -f context/Containerfile -t ee-test context

Testing the created EE container

The image can be started as any other podman image, installed collections should be under the path /usr/share/ansible/collections/ansible_collections at container level:

[ubuntu:~/my-builder-env/context]$ podman images

REPOSITORY                                                                            TAG         IMAGE ID      CREATED        SIZE
localhost/ee-test                                                                     v1.0        a524ff8039ed  3 minutes ago  678 MB                                                            v0.9.1      d1524b4410d0  2 months ago   1.4 GB       latest      19186d4e0970  5 months ago   372 MB  latest      26582aeee896  5 months ago   340 MB

Note: to list the existent EE containers the ansible-navigator command can be used:

[ubuntu:~]$ ansible-navigator images {-m stdout} 

The created image has the IMAGE_ID a524ff8039ed:

[ubuntu:~/my-builder-env]$ podman run -it a524ff8039ed /bin/bash

[root@82cd58122b15 /]# ls /usr/share/ansible/collections/ansible_collections
amazon	community

[root@82cd58122b15 /]# python3 --version
Python 3.8.13

[root@82cd58122b15 /]# git --version
git version 2.31.1

Let’s create a playbook to use, from the added community.general collection (see requirements.yml file), the community.general.python_requirements_info module.

[ubuntu:~ ]$ cd $HOME

[ubuntu:~ ]$ mkdir test

[ubuntu:~/test]$ cat test_playbook.yml
- name: Testing a custom Ansible Content Collection
  hosts: localhost

    - name: Show python lib/site paths
      register: answer

    - name: print output
        msg: "{{ answer }}"

Run the test playbook test_playbook.yml:

[ubuntu:~/test]$ ansible-navigator run test_playbook.yml  --eei localhost/ee-test:v1.0 --pp never -b -m stdout

[WARNING]: provided hosts list is empty, only localhost is available. Note that
the implicit localhost does not match 'all'

PLAY [Testing a custom Ansible Content Collection] *****************************

TASK [Gathering Facts] *********************************************************
ok: [localhost]

TASK [Show python lib/site paths] **********************************************
ok: [localhost]

TASK [print output] ************************************************************
ok: [localhost] => {
    "msg": {
        "changed": false,
        "failed": false,
        "mismatched": {},
        "not_found": [],
        "python": "/usr/bin/python3.8",
        "python_system_path": [
        "python_version": "3.8.13 (default, Jun 14 2022, 17:49:07) \n[GCC 8.5.0 20210514 (Red Hat 8.5.0-13)]",
        "python_version_info": {
            "major": 3,
            "micro": 13,
            "minor": 8,
            "releaselevel": "final",
            "serial": 0
        "valid": {}

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


Publish the container to the hub

Let’s tag our EE image (not mandatory but nice )

[ubuntu:~/test]$ podman tag localhost/ee-test:v1.0

[ubuntu:~/test]$ podman images | grep ee-test                                             latest      a524ff8039ed  8 days ago    678 MB
localhost/ee-test                                                                     v1.0        a524ff8039ed  8 days ago    678 MB

Push the image to the hub:

[ubuntu:~/test]$ podman login
Username: [email protected]

[ubuntu:~/test]$ podman push


Using ansible tools from a container give the possibility to standardise even more the environnements deployment. As you can see, there is nothing new under the sun. Build a podman/docker container never need RedHat ansible dedicated tools. What is new, is that using ansible-builder, give you the possibility to start from a standard container which is supposed to follow all the best practice. The opening sentence, that EE “is a standard way to build and distribute the environment that automation runs in, which reduces complexity and makes it faster and simpler to develop and deploy automation.” takes on its full meaning.

Share on