In a Terraform aws provider block, why should you not set access_key and secret_key, and where does the provider get credentials instead?
answer
- code is copied; credentials must be revocable
- hiding the value is not removing it
- the provider has its own credential chain
- short-lived role sessions beat static keys
- the provider block should hold no secrets
basics
~20 sKeys written into HCL get committed, cloned and copied, and cannot be rotated per run. Omit access_key and secret_key; the AWS provider then resolves credentials itself from environment variables, the shared AWS config files, or an attached instance or CI role.
solid answer
~50 sTerraform configuration is source code: it is reviewed, cloned, mirrored and kept in history forever. A long-lived `access_key`/`secret_key` pair pasted into a `provider "aws"` block therefore leaks to everyone who can read the repository, survives deletion because git keeps history, and cannot be scoped or rotated per run. The fix is not to hide the value better — a `secret.auto.tfvars` file or a `sensitive` variable still ends up as a long-lived static key sitting on disks and in CI logs. The fix is to stop putting credentials in the configuration at all. Leave the provider block to non-secret settings such as `region` and `default_tags`, and let the provider resolve credentials from its own chain: `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY`/`AWS_SESSION_TOKEN` environment variables, a named profile in the shared config files, or, best of all, short-lived credentials from an attached instance role or an OIDC-federated CI role.
go deeper
Be able to say plainly that Terraform files are committed source code, so a key placed in one is shared and permanent, and that you leave the credential arguments out and supply credentials through the environment or a profile instead.
Explain the provider's credential chain concretely — environment variables, shared config profiles, instance and container roles — and why moving the key into a variable or a gitignored file changes visibility but not the underlying long-lived-secret problem.
Show you would treat a pushed key as compromised and rotate at the issuer rather than force-push, and describe how you get an estate to zero static keys: role sessions on the machines, federated identity in CI, per-engineer profiles locally.
Own the policy: what identity each Terraform execution context runs as, how those identities are scoped and audited, and what control actually prevents a key from reappearing — pre-commit and CI secret scanning plus not issuing long-lived keys in the first place.
## The shape of the mistake Every Terraform provider needs credentials for the API it talks to, and every provider offers arguments to supply them directly. For the AWS provider those are `access_key`, `secret_key` and `token`. They exist, they work, and using them is almost always wrong. The reason is not that the arguments are broken; it is that Terraform configuration is *code*. It is committed, reviewed in pull requests, cloned onto laptops, mirrored to CI caches, and archived in git history. A credential placed there inherits all of those properties. It is now readable by everyone with repository access, including people who joined after the commit. It stays readable after you delete the line, because the old blob is still in history. And it is a *long-lived* credential — an IAM user access key does not expire on its own — so a copy taken today still works next year. ## The near-miss fixes that are not fixes Candidates usually reach for one of these: - **Move it to a `.tfvars` file and gitignore it.** Better than committing, but you still have a permanent static key on every developer machine and in the CI secret store, shared by everyone, rotated by nobody. - **Make it a variable marked `sensitive = true`.** `sensitive` only redacts the value in Terraform's own CLI output and plan rendering. It does not encrypt anything, does not stop the value being passed on a command line or through `TF_VAR_` environment variables, and does not change the fact that the key is long-lived. - **Delete the commit and force-push.** The blob may still exist in forks, clones, CI caches and provider-side git storage. Once a credential has been pushed, the only real remediation is revocation and rotation at the issuer. None of these change the underlying property: a static key exists, and it is portable. ## What replaces it Omit the credential arguments entirely and let the provider run its resolution chain. A healthy provider block carries only non-secret configuration: ```hcl provider "aws" { region = "eu-west-1" default_tags { tags = { ManagedBy = "terraform" } } } ``` Credentials then come from outside the repository, in roughly this order of preference: 1. **A role attached to the compute that runs Terraform.** An EC2 instance profile, an ECS task role, or an EKS pod identity. The provider fetches short-lived credentials at runtime; nothing is stored anywhere. 2. **OIDC federation from CI.** The CI platform issues a short-lived signed token identifying the job, and the provider exchanges it for a temporary AWS role session. No key exists to steal. 3. **A named profile in the shared config files** (`~/.aws/config`, `~/.aws/credentials`), typically backed by SSO or an assumed role. Convenient for humans; each engineer has their own identity, which is what you want for audit. 4. **Environment variables** (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`) — fine when they carry *temporary* session credentials, a last resort when they carry a static key. All four keep the credential out of the code and, in the first three cases, make it short-lived and attributable to a specific identity. ```bash # credentials come from outside the configuration export AWS_PROFILE=platform-dev terraform plan ``` ## The point an interviewer is listening for Two things. First, that you name the real property that makes hardcoding bad — a code artifact is copied and retained, a credential must be revocable and short-lived, and those two requirements are incompatible. Second, that your remedy is *removal*, not concealment. "Put it in a variable" is the answer of someone who thinks the problem is visibility; "let the provider pick up a role session" is the answer of someone who understands the problem is the existence of a durable secret. One related fact worth carrying: this is about the *provider's* configuration, not about Terraform state. Credentials used to authenticate are not what lands in state — but resource attributes such as generated passwords are, which is a separate concern with its own handling.
- Someone argues the key is safe because it is in a gitignored terraform.tfvars rather than in the provider block. What do you say?That it improves visibility hygiene and nothing else. The credential is still a long-lived static key living on every machine that runs Terraform and in the CI secret store, shared and rarely rotated. The property that matters is lifetime and revocability, not which file holds it. Replace it with a role session the runtime obtains at execution time.
- A credential was committed six months ago and has since been removed from the file. Is the repository clean?No. The blob is still reachable in git history, and likely in clones, forks and CI caches. History rewriting does not reach those copies. Treat the credential as compromised: revoke and rotate it at the issuer, then check usage logs for the period it was exposed. Cleaning history is optional tidy-up, not remediation.
- Does marking a Terraform variable sensitive protect a credential?Only in Terraform's own output. `sensitive = true` suppresses the value in plan and apply rendering and in `terraform output`. It does not encrypt the value, does not stop it reaching provider API calls, and does not prevent it being visible wherever it was supplied from. It is display hygiene, not a secret-management control.
saying these in an interview costs you the question
- It is fine because the repository is private
- Terraform cannot authenticate unless keys are in the config
- Putting the key in a gitignored tfvars file solves it
- Marking the variable sensitive protects the credential
- Deleting the commit removes the exposure