Managing secrets across infrastructure-as-code and configuration management tools usually means duplicating credentials in provider blocks, variable files, or inventory vaults. Every copy is another chance for a credential to leak, expire, or fall out of sync. Pulumi ESC (Environments, Secrets, and Configuration) offers a cleaner pattern: a central environment that acts as the source of truth, then lets you sync credentials across Ansible inventories and Terraform providers without embedding them in code or static files. This walkthrough shows you how to build that setup today, with fresh attention to the workload-identity and short-lived-credential patterns that matter in 2026.
Why a Central Secret Plane Beats Scattered Credentials
Terraform provider configurations usually expect environment variables like AWS_ACCESS_KEY_ID, AZURE_CLIENT_SECRET, or GOOGLE_CREDENTIALS. Ansible inventories, on the other hand, often reference Vault-encrypted files or group variables such as ansible_user and ansible_ssh_pass. Keeping these two worlds in sync manually is tedious, and drift leads to failed plans or lockouts.
Using a single ESC environment gives you three practical benefits:
- One write path: rotate a secret once in ESC and every downstream consumer picks it up.
- Layered configuration: separate static secrets, dynamic secrets, and non-secret configuration values, then export exactly what each tool needs.
- Auditability: ESC event logs show when a secret was opened and which environment or command requested it, which is invaluable during compliance reviews.
What an ESC Environment Contains in Practice
An ESC environment is a YAML document that defines variables in layers. You can nest imports, merge values, and mark certain keys as secrets. For the Terraform-and-Ansible bridge, the environment typically includes three sections:
- Static secrets: API tokens, database passwords, and SSH private keys stored encrypted by Pulumi.
- Dynamic secrets: credentials fetched at runtime from AWS OIDC, Azure AD, or HashiCorp Vault, which expire automatically.
- Project-specific values: inventory names, provider regions, or default prefixes that both tools can share.
A minimal environment might look like this:
values:
aws:
accessKeyId: ${secrets.aws.accessKeyId}
secretAccessKey: ${secrets.aws.secretAccessKey}
sessionToken: ${secrets.aws.sessionToken}
ansible:
inventoryBucket: "acme-prod-inventory"
sshUser: "deploy"
The imported secrets are encrypted at rest, and exposed only when a command uses the environment.
Syncing Credentials to Terraform Providers
The easiest integration point for Terraform is the esc run command. Instead of exporting secrets manually, you wrap your normal workflow:
esc run acme/production -- terraform plan
ESC reads the environment, resolves all dynamic secrets, and injects the values as environment variables into the Terraform process. Because provider blocks detect credentials from the environment by default, no code changes are required.
For teams running Terraform in CI, this pattern works just as well. Define an OIDC integration between the CI platform and your ESC environment, then call esc run from the pipeline. The result: no long-lived keys in CI variables, and provider credentials that automatically rotate. If a credential is revoked, every pipeline using that environment breaks cleanly instead of failing halfway through an apply.
An important detail for multi-region or multi-account environments is environment layering. You can create a production environment that imports a baseline global environment, then overrides region-specific values. Terraform consumes the merged result, keeping the provider configuration consistent across directories.
Bringing Ansible Inventories into the Same Loop
Ansible’s strength is the inventory, but its secret handling is traditionally fragmented. You can bridge it by using ESC as an inventory source and a credentials provider at the same time.
One approach is the environment variable method. Prefix your Ansible command with esc run so the playbook process inherits credentials:
esc run acme/production -- ansible-playbook -i inventory/production playbooks/site.yml
If your playbooks rely on ansible_user and ansible_ssh_private_key_file, define those values in the ESC environment and map them to environment variables using variable aliases or shell overrides in a wrapper inventory file.
A more advanced pattern is the custom dynamic inventory plugin. Pulumi ESC has a CLI plugin architecture, and you can write a small inventory script that calls pulumi esc open to fetch the current environment, then returns hosts, groups, and variables as JSON. This keeps credentials out of Vault files entirely and makes the inventory itself a derived output of the same environment that drives Terraform. When the inventory changes, your playbook logic stays unchanged.
For teams using Ansible Tower or AWX, the same principle works with a custom credential type that calls the ESC API. Instead of storing SSH keys in Tower’s credential vault, you store an ESC environment name and let the controller fetch the secret at job run time.
Rotating and Importing Secrets Without Downtime
A practical benefit of ESC is that rotation becomes a coordinated operation rather than a big-bang migration. Suppose a database password rotates. You update the secret in the ESC environment, then run pulumi esc env set or make a pull request on the environment’s YAML. The next Terraform plan and the next Ansible playbook both use the new value. There is no window in which one tool has rotated and the other has not.
You can also import an existing secret from AWS Secrets Manager or Vault instead of re-pasting values. ESC supports native integration with multiple providers, so legacy bytes can be absorbed into an environment without forcing a credential reset. For organizations migrating from HashiCorp Vault, this hybrid setup keeps older infrastructure working while new workflows move to ESC-native dynamic secrets.
What This Setup Enables in 2026
The older mindset of copying secrets into each tool is giving way to ephemeral, federated access. In 2026, the most resilient setups combine ESC with short-lived workload identities to remove static keys from disk entirely. Terraform can use OIDC-federated credentials through ESC, and Ansible can pull SSH certificates or cloud instance data from a broker that refreshes them hourly. The result is a server fleet that never stores a permanent secret and a Terraform state that never contains raw provider credentials.
That shift also improves incident response. When a security alert fires, you can disable a single ESC environment or add a policy that blocks access from a specific IP range. The same action immediately affects both Terraform and Ansible because they both read from the same source. You avoid the usual scramble to find every filesystem copy of a leaked credential.
Operationally, the setup also scales from one developer to a large platform team. Developers open their local esc run sessions with the permissions they need, while CI and production use separate environments with stricter access policies. The line between “developer” and “server” access becomes intent-driven rather than a mess of shared keys.
A Short Conclusion
Pulumi ESC gives infrastructure teams a practical way to sync credentials across Ansible inventories and Terraform providers without scattering secrets across codebases. By defining environments once and consuming them through esc run or dynamic inventory plugins, you reduce drift, improve auditability, and enable modern short-lived credential workflows. Whether you start with static secrets or move directly to federated OIDC access, the central source of truth is the same: an environment that both tools can trust.
