Fresh custom EE - SS67/project-docs GitHub Wiki

Recall from the transcript. The exact Containerfile and collections file we built are there, but the actual podman build and push commands were run on your end (never came up in our chats), so I will reconstruct the standard workflow to match the image tag we settled on: aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0.

Files we built

execution-environment/Containerfile

FROM registry.access.redhat.com/ubi9/ubi:latest

RUN dnf clean all && \
    dnf install -y --allowerasing \
    gcc \
    make \
    git \
    openssl-devel \
    bzip2-devel \
    libffi-devel \
    xz-devel \
    wget \
    zlib-devel \
    sqlite-devel \
    tar \
    krb5-devel \
    libssh-devel \
    python3-devel \
    openssl \
    glibc-langpack-en \
    libxml2-devel \
    libxslt-devel \
    && dnf clean all && \
    rm -rf /var/cache/dnf/*

WORKDIR /opt
RUN wget https://www.python.org/ftp/python/3.12.2/Python-3.12.2.tgz && \
    tar -xzf Python-3.12.2.tgz && \
    cd Python-3.12.2 && \
    ./configure --enable-optimizations && \
    make altinstall && \
    cd /opt && rm -rf Python-3.12.2 Python-3.12.2.tgz

RUN python3.12 -m ensurepip --upgrade && \
    python3.12 -m pip install --upgrade pip setuptools wheel && \
    python3.12 -m pip install --no-cache-dir \
    ansible-core==2.16.14 \
    ansible-runner \
    pyvmomi==8.0.3.0.1 \
    requests==2.32.3 \
    hvac==2.3.0 \
    jmespath==1.0.1 \
    git+https://github.com/vmware/vsphere-automation-sdk-python.git \
    && python3.12 -m pip cache purge

ENV LANG=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8

RUN alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.12 1 && \
    alternatives --install /usr/bin/pip3 pip3 /usr/local/bin/pip3.12 1

RUN ansible-galaxy collection install \
    community.vmware:>=3.0.0 \
    community.hashi_vault:>=6.2.0 \
    community.general:>=9.0.0 \
    ansible.posix:>=1.5.0 \
    ansible.utils:>=5.0.0 \
    -p /usr/share/ansible/collections

collections/requirements.yml

collections:
  - name: vmware.vmware_rest
    version: 4.6.0
  - name: community.hashi_vault
    version: 6.2.0
  - name: ansible.utils
    version: 5.1.0
  - name: ansible.posix
    version: 1.6.2

Note that the Containerfile installs collections directly during build (via ansible-galaxy) and collections/requirements.yml is what your playbooks reference at runtime. Both approaches exist because the Containerfile bakes them into the image, while the requirements.yml is used by AAP project sync.

Build and push workflow (standard for this image tag)

From the repo root on a build host with podman installed and login to your internal registry:

# 1. From repo root, build with the Containerfile
podman build \
  -f execution-environment/Containerfile \
  -t aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0 \
  .

# 2. Verify the image
podman images | grep fresh-vc-sync
podman inspect aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0 | jq '.[0].Config.Labels'

# 3. Login to your internal registry
podman login aap-service.company.com

# 4. Push
podman push aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0

# 5. Verify pull works from another node
sudo podman pull aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0

Smoke test the EE before pushing

Worth doing before wiring into AAP:

# Test ansible-core version
podman run --rm aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0 \
  ansible --version

# Test pyvmomi and hvac import
podman run --rm aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0 \
  python3 -c "import pyVim.connect, hvac, requests; print('imports ok')"

# Test collection presence
podman run --rm aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0 \
  ansible-galaxy collection list | grep -E 'vmware|hashi_vault|posix|utils'

Wire into AAP

Under Administration → Execution Environments → Add:

  • Name: fresh-vc-sync-ee
  • Image: aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:1.0
  • Pull: Missing (or Always if you re-tag :1.0 in place)
  • Credential: registry credential for aap-service.company.com

Then set it as the default EE on the job templates and inventory source that need it.

Rebuild flow when you need to update

# Bump version tag
NEW_TAG=1.1

# Rebuild
podman build \
  -f execution-environment/Containerfile \
  -t aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:$NEW_TAG \
  .

# Also tag as latest if you want a moving pointer
podman tag \
  aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:$NEW_TAG \
  aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:latest

# Push both
podman push aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:$NEW_TAG
podman push aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:latest

Then update the EE definition in AAP to point at the new tag, or if you use :latest, force a re-pull on the execution nodes.

Two things worth flagging on the Containerfile as it stands

  1. Building Python 3.12 from source adds 15-20 min to build time. UBI 9 ships with Python 3.9 by default. We went the source-build route because pyvmomi 8.0.3 wanted 3.12 features. If you rebuild often, worth exploring the ubi9/python-312 prebuilt image as the base to skip the compile step. Would trim build time to a few minutes.

  2. Collections are installed twice, once in the Containerfile (via ansible-galaxy inside the image) and once at runtime (via requirements.yml during project sync). Runtime install adds a few seconds per job. Not a real problem but redundant. If you clean this up later, either pick the Containerfile version (fastest, but requires rebuild for collection updates) or the requirements.yml version (slower per job but easier to update).

Both are minor operational polish items, not blockers. The current setup works.

Not ideal. It works, but it's overweight and non-standard.

What we have

Direct Containerfile with hand-rolled Python 3.12 from source on UBI 9. ~2-3GB image, 20-25 min build. Custom, hard to maintain.

Better options ranked

1. ansible-builder with UBI 9 minimal (best, most idiomatic)

Anthropic's official tool for AAP EEs. You write a small execution-environment.yml, it generates the Containerfile. Uses registry.redhat.io/ansible-automation-platform-26/ee-minimal-rhel9 as base which ships with Python 3.11, ansible-core, and the AAP runtime pre-installed.

execution-environment.yml:

version: 3
images:
  base_image:
    name: registry.redhat.io/ansible-automation-platform-26/ee-minimal-rhel9:latest
dependencies:
  ansible_core:
    package_pip: ansible-core==2.16.14
  python:
    - pyvmomi==8.0.3.0.1
    - hvac==2.3.0
    - requests==2.32.3
    - jmespath==1.0.1
  galaxy: collections/requirements.yml
  system:
    - krb5-devel
    - libxml2-devel

Build:

ansible-builder build \
  -t aap-service.company.com/fresh-vc-sync/fresh-vc-sync-ee:2.0 \
  --container-runtime podman

Result: ~800MB image, 5-8 min build, standard Red Hat tooling, upgradeable when AAP versions bump.

2. ee-supported-rhel9 base (if you need extra vendor support) Same pattern, larger base with additional Red Hat certified collections pre-installed. Only worth it if you also use those. Otherwise minimal is better.

3. Skip Python 3.12 requirement entirely Python 3.11 in ee-minimal handles pyvmomi 8.0.3 fine. The 3.12 requirement in your current build was overkill.

Recommendation

Switch to ansible-builder + ee-minimal-rhel9. Smaller, faster, standard, easier to maintain. Version bumps become editing one yaml line.

Migration is low risk. Build side by side as :2.0, smoke test, cut over EE definition in AAP.