Azure Container
Everything a customer Azure environment must provide to run the ansible-epic
control node as an Azure Container Instances container group — the compute
itself plus the registry, storage, identity, secret, and network dependencies
around it. This page is the full deployment picture behind the summary bullets
in Requirements → Azure Container; it slots
into steps 1–2 of the New Customer Checklist.
Access-mode and image details live in Containers.
Compute
- One container group running
ansible-epic/base:latest— 2 vCPU / 4 GB is the working default — with:- a private IP in the delegated container subnet (no public IP);
- TCP 22 exposed (SSH access mode);
- the customer's persistent file share mounted at
/home/ansible/source; - the user-assigned identity (below) attached;
- registry credentials via that identity (no admin passwords);
- environment variables per the chosen
access mode (e.g.
AUTHORIZED_KEYSfor SSH,TUNNEL_NAMEfor a VS Code tunnel) andUSER_SETUP=1if the per-user setup and persistence feature is wanted.
- Pin the group to an availability zone if the customer's platform standards require zonal placement.
Container registry
- An Azure Container Registry in the customer environment, with an
artifact cache rule
mapping
ghcr.io/sapphire-health/ansible-epic/baseto a local repository. The image then mirrors on demand — no manual push pipeline. The GHCR packages are public, so the cache rule needs no upstream credential. - Be aware of the cache's
latest-tag revalidation lag: a re-publishedlatestupstream can take time to be re-fetched. - The container's identity needs
AcrPullon this registry.
Persistent storage
- A storage account with an
ansible-filesshare (32 GB is a comfortable start) for/home/ansible/source— playbooks, inventory, and per-user state survive container restarts. -
Do not use a default (Standard) storage account — standard file shares have caused real performance problems for this workload (git operations and Ansible runs hit thousands of small-file IOPS). The account must be created with these settings, which can't be changed after creation:
Setting Required value account_tierPremiumaccount_kindFileStorage(SSD file shares —Premiumtier alone isn't enough if the kind isStorageV2)account_replication_typeLRSprovisioned_billing_model_versionV2(provisioned IOPS/throughput scale independently of quota) -
The storage account must be in the same region as the container group — a cross-region SMB mount adds latency to every file operation and can fail ACI's mount at group creation.
- Public network access disabled on the storage account; access is via a
fileprivate endpoint in a non-delegated subnet, with aprivatelink.file.core.windows.netprivate DNS zone linked to the vnet so the mount resolves privately. - The container subnet needs the
Microsoft.Storageservice endpoint for the ACI file-share mount.
Key Vault and the vault password
- A Key Vault that lives outside the deployment's lifecycle — its own
resource group, never in any automated destroy path. It holds the
ansible-vault-passwordsecret, which the container reads on every playbook run (scripts/azure-vault-pass.sh→az login --identity→ secret get; see Secret Management). - The secret's value should be generated, never typed or committed — and must be retrievable only from the vault (long and random; the encrypted vault files' ciphertext is only as strong as this password).
- If the vault is network-restricted, it needs its own private endpoint and
privatelink.vaultcore.azure.netDNS zone reachable from the container subnet.
Identity and permissions
A user-assigned managed identity for the container, with:
| Grant | Purpose |
|---|---|
AcrPull on the registry |
Pull the cached image |
Key Vault Secrets User (or access-policy get) on the vault |
Read ansible-vault-password at runtime |
| Reader on every subscription with managed hosts | The azure_rm dynamic inventory enumerates VMs |
Whoever performs the deployment needs, beyond resource-creation rights in the subscription:
- rights to create role assignments (
User Access Administratoror Owner) for the grants above; - Key Vault Secrets Officer on the vault, to seed the
ansible-vault-passwordsecret.
Network
- A vnet with two subnets:
- a container subnet carrying the exclusive
Microsoft.ContainerInstance/containerGroupsdelegation (nothing else can share it) plus theMicrosoft.Storageservice endpoint; - a general subnet for the private endpoints (they can't land in the delegated subnet).
- a container subnet carrying the exclusive
- Reachability from the container subnet to every managed host: 22 to Linux, 5985/5986 to Windows — see Requirements → Network. ACI containers can change IP across restarts, so scope these rules to the delegated subnet, never the container's current IP.
- An NSG on the container subnet admitting inbound SSH (22) — and HTTPS (443) if a web access mode is used — from the customer's management ranges only.
- Outbound, the container subnet must reach the ACR endpoint (image pull),
the Key Vault endpoint (secret reads), and the storage account's private
endpoint (share mount). No
ghcr.ioegress is needed — the registry cache fetches upstream on ACR's side.
Validating a deployment
From inside the running container:
az login --identitysucceeds (identity attached and functional);- the vault password script prints the secret (Key Vault grant + network path);
ansible-inventory --listenumerates the customer's VMs (subscription Reader + inventory config);- a file written under
/home/ansible/sourcesurvives a container restart (share mounted, private endpoint resolving); ansible -m ping/-m win_pingreach a Linux and a Windows host (network reachability rules).
Then continue the New Customer Checklist from step 3 (repository, inventory, vault contents, first playbook run).