by Alexandre Nestor
Introduction
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 Name | Container image | Description |
---|---|---|
Minimal | ee-minimal-rhel8 | A minimal automation execution environment based on Ansible Core 2.11. only includes the ansible.builtin |
Supported | ee-supported-rhel8 | An automation execution environment based on Ansible Core 2.11 that includes Red Hat Ansible Certified Content Collections and their software dependencies. |
Compatibility | ee-2rhel9 | An 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
DISTRIB_DESCRIPTION="Ubuntu 22.04.1 LTS"
[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: quay.io/ansible/creator-ee:v0.9.1
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 quay.io/ansible/creator-ee:v0.9.1
Trying to pull quay.io/ansible/creator-ee:v0.9.1...
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]$
[ubuntu:~/my-builder-env]$ cat execution-environment.yml
---
version: 1
build_arg_defaults:
EE_BASE_IMAGE: registry.redhat.io/ansible-automation-platform-20-early-access/ee-minimal-rhel8
EE_BUILDER_IMAGE: registry.redhat.io/ansible-automation-platform-20-early-access/ansible-builder-rhel8
ansible_config: ansible.cfg
dependencies:
galaxy: requirements.yml
python: requirements.txt
system: bindep.txt
additional_build_steps:
prepend:
- ADD my_tuf /usr/share/my/stuff
append:
- COPY ....
EE_BASE_IMAGE
: this is the container image to use as starting point. For public access usequay.io/ansible/ansible-runner:stable-2.11-latest
.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
andbindeb.txt
files content:
[ubuntu:~/my-builder-env]$ cat ansible.cfg
[galaxy]
server_list = galaxy
[galaxy_server.galaxy]
url=https://galaxy.ansible.com/
# Add community.aws and community.general to the EE container
[ubuntu:~/my-builder-env]$ cat requirements.yml
---
collections:
- community.aws
- community.general
# Add json and requests to the EE container.
[ubuntu:~/my-builder-env]$ cat requirements.txt
json
requests
# Add rsync and git to the EE container.
[ubuntu:~/my-builder-env]$ cat bindep.txt
rsync
git
zsh
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 quay.io/ansible/ansible-runner:stable-2.11-latest
instead.
[ubuntu:~/my-builder-env]$ podman login registry.redhat.io
Username: [email protected]
Password:
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:
- The command creates the
context/
directory in the current directory. In that directory, it creates theContainerfile
file, the equivalent of aDockerfile
file, which contains instructions for thepodman build
command. It also creates an_build/
subdirectory and then copies yourrequirements.yml
,ansible.cfg
,requirements.txt
, andbindep.txt
files into it, so that the build process can access them. - 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
ARG EE_BASE_IMAGE=registry.redhat.io/ansible-automation-platform-20-early-access/ee-minimal-rhel8
ARG EE_BUILDER_IMAGE=registry.redhat.io/ansible-automation-platform-20-early-access/ansible-builder-rhel8
FROM $EE_BASE_IMAGE as galaxy
ARG ANSIBLE_GALAXY_CLI_COLLECTION_OPTS=
ARG ANSIBLE_GALAXY_CLI_ROLE_OPTS=
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"
FROM $EE_BUILDER_IMAGE as builder
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
FROM $EE_BASE_IMAGE
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
quay.io/ansible/creator-ee v0.9.1 d1524b4410d0 2 months ago 1.4 GB
registry.redhat.io/ansible-automation-platform-20-early-access/ee-minimal-rhel8 latest 19186d4e0970 5 months ago 372 MB
registry.redhat.io/ansible-automation-platform-20-early-access/ansible-builder-rhel8 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
tasks:
- name: Show python lib/site paths
community.general.python_requirements_info:
register: answer
- name: print output
ansible.builtin.debug:
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": [
"/tmp/ansible_community.general.python_requirements_info_payload_xand6vtb/ansible_community.general.python_requirements_info_payload.zip",
"/usr/lib64/python38.zip",
"/usr/lib64/python3.8",
"/usr/lib64/python3.8/lib-dynload",
"/usr/local/lib64/python3.8/site-packages",
"/usr/local/lib/python3.8/site-packages",
"/usr/lib64/python3.8/site-packages",
"/usr/lib/python3.8/site-packages"
],
"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
[ubuntu:~/test]$
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 myhub.example.com/mytestnamespace/ee-test:latest
[ubuntu:~/test]$ podman images | grep ee-test
myhub.example.com/mytestnamespace/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 myhub.example.com
Username: [email protected]
Password:
[ubuntu:~/test]$ podman push myhub.example.com/mytestnamespace/myhub.example.com/mytestnamespace/ee-test:latest
Conclusion
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.