Building a Custom Execution Environment with Claude Code and a Private Automation Hub

I’ve been building Execution Environments (EEs) for Ansible Automation Platform for a while now, but this time I wanted to see how far I could get by handing the whole thing to Claude Code and just reacting to what came back. I’m using the Ansible Automation Platform version 2.6 for this demo, and I’m putting certified and validated content into the custom EE. I’ve noticed that about 75% of the time with a custom EE I run into issues that takes me a few iterations to fix, so I wanted to farm out the work to AI.
I have a blog post on syncing certified/verified collections into Private Automation Hub here.
The Starting Point
This is the execution-environment.yml file I use with ansible-builder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | ---
version: 3
images:
base_image:
name: registry.redhat.io/ansible-automation-platform-26/ee-minimal-rhel9:latest
dependencies:
system:
- python3
- python3-pip
- python3-setuptools
- python3-wheel
- python3-devel
- gcc
- krb5-devel
- krb5-libs
- systemd-devel
galaxy:
collections:
- name: ansible.platform
version: "2.6.20260504" # match your AAP 2.6 target
- name: ansible.hub
version: ">=1.0.0"
- name: ansible.controller
version: ">=4.7.0" # 4.7.0+ for AAP 2.6
- name: ansible.eda
version: ">=2.5.0"
- name: infra.aap_configuration
python:
- python-dateutil>=2.7.0
- awxkit
- pyyaml>=6.0.1
options:
package_manager_path: /usr/bin/microdnf
additional_build_steps:
prepend_galaxy:
- ENV ANSIBLE_GALAXY_SERVER_LIST=rh_certified,validated
- ENV ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_URL=https://aap.example.com/api/galaxy/content/rh-certified/
- ENV ANSIBLE_GALAXY_SERVER_VALIDATED_URL=https://aap.example.com/api/galaxy/content/validated/
- ARG ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_TOKEN
- ENV ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_TOKEN=$ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_TOKEN
- ARG ANSIBLE_GALAXY_SERVER_VALIDATED_TOKEN
- ENV ANSIBLE_GALAXY_SERVER_VALIDATED_TOKEN=$ANSIBLE_GALAXY_SERVER_VALIDATED_TOKEN
- ENV ANSIBLE_GALAXY_DISABLE_GPG_VERIFY=1 |
The magic happens at the end where I’m setting up the environment variables to grab the tokens for my certified and validated repos. The old way was to hard code this in an ansible.cfg file, but you really don’t want to pass that to your AI, so doing it this way keeps things clean.
Notice the split: the URLs are plain ENV values baked right into the YAML, because they’re not sensitive. The tokens are declared as ARG first, then re-exported as ENV of the same name because ansible-galaxy only reads environment variables, not build args directly. The actual token value never touches this file; it only gets supplied at build time via --build-arg.
This matters for another reason too: ansible-builder generates a multi-stage Containerfile. The stage that installs collections (galaxy) is discarded the final image only does a plain filesystem COPY --from=galaxy /usr/share/ansible /usr/share/ansible, not a full FROM galaxy. Docker/Podman ENV/ARG values are stage-scoped, so the token doesn’t end up as an inspectable environment variable in the shipped image. It does still exist transiently in that stage’s build cache layer, which is worth knowing if your threat model cares about that.
Problem 1: TLS Certificate Verification
Once auth was wired up, the build got further but hit an SSL error reaching the PAH:
1 | CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate |
My PAH uses an internal CA that isn’t in the trust store of the minimal RHEL9 build image. The “proper” fix is importing the CA cert into the image’s trust store before the collection install step. The fast fix appropriate for an internal, already-authenticated private Hub is telling `ansible-galaxy` to skip cert validation for that call. Ansible Builder already exposes a hook for this via the ANSIBLE_GALAXY_CLI_COLLECTION_OPTS build-arg, which gets appended straight onto the ansible-galaxy collection install command:
1 2 3 | ansible-builder build --tag infra-cac \ --build-arg ANSIBLE_GALAXY_CLI_COLLECTION_OPTS="--ignore-certs" \ ... |
Keeping your token secure
Claude refused to build with the token exposed to it…which is what you would want, but I still need it to interact with ansible-builder. At several points the build needed a live Automation Hub token passed via --build-arg.
The trick is a tiny wrapper script that sources a local, gitignored secrets file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/usr/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SECRETS_FILE="$SCRIPT_DIR/.secrets/hub-tokens.env"
if [ ! -f "$SECRETS_FILE" ]; then
echo "Missing $SECRETS_FILE" >&2
exit 1
fi
source "$SECRETS_FILE"
cd "$SCRIPT_DIR"
ansible-builder build --tag infra-cac \
--build-arg ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_TOKEN="$ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_TOKEN" \
--build-arg ANSIBLE_GALAXY_SERVER_VALIDATED_TOKEN="$ANSIBLE_GALAXY_SERVER_VALIDATED_TOKEN" \
--build-arg ANSIBLE_GALAXY_CLI_COLLECTION_OPTS="--ignore-certs" |
And the secrets file itself, created by hand not by the AI, and never pasted into the chat with restrictive permissions:
1 2 3 4 | umask 077 && cat > .secrets/hub-tokens.env <<'EOF' export ANSIBLE_GALAXY_SERVER_RH_CERTIFIED_TOKEN='your-token-here' export ANSIBLE_GALAXY_SERVER_VALIDATED_TOKEN='your-token-here' EOF |
From that point on, all I had to tell Claude was “run build.sh” it never saw, typed, or logged the actual secret. The file path is the only thing that ever appears in a command. Add .secrets/ to .gitignore and you’ve got a repeatable, safe build process that an AI agent can drive end-to-end.
Cleaning Up After Yourself
Every failed build attempt leaves behind a dangling, untagged image layer. After a dozen or so iterations chasing the errors above, podman image list was full of <none><none> entries eating disk space. Since these aren’t referenced by any tagged image, they’re safe to clear in one shot:
1 | podman image prune -f |
Conclusion
I can now quickly build my execution-environment.yml file, and hand it off to claude code build VM to just crank away and iterate until it can successfully build the EE…what a time to be alive LOL.
If you have any questions, comments, or modifications, drop me a line.
Good luck and happy EE creation.

