Skill 08 · Provider Configuration
Subchapter 8.1
references/case-studies.mdMarkdown6 KBView on GitHub
Two production designs at opposite ends of the complexity scale. Read these
to calibrate how much chain your provider actually needs — then implement
with the generic pattern in credential-chain.md.
The Terraform AWS provider, the AWSCC provider, and the S3 backend all
resolve credentials through one shared library,
hashicorp/aws-sdk-go-base (opens in a new tab)
(MPL-2.0). Its entry point GetAwsConfig(ctx, *Config) is the most
battle-tested credential chain in the Terraform ecosystem.
assume_role_with_web_identity
block replaces whatever the default chain produced with an STS
web-identity provider (validated: role ARN required, exactly one token
source).assume_role is a list; each entry wraps the
previously resolved credentials in an STS assume-role provider, enabling
role chaining (credentials → role A → role B). Each hop carries its own
session name, external ID, policy, tags, and duration, wrapped in a
credentials cache.Retrieve() is called
during configuration, and — unless skip_credentials_validation is set —
an sts:GetCallerIdentity call proves the credentials actually work.
Failures surface at plan time with configuration-shaped errors instead of
mid-apply with API-shaped ones.NoValidCredentialSourcesError. The missing-credentials diagnostic
embeds a caller-supplied documentation URL (CallerDocumentationURL) and
the underlying error. Every downstream product (provider, backend) points
users at its own auth docs through the same error type.profile and static env-var credentials
are present, it emits a “configuration conflict” warning explaining which
source took precedence — and if resolution then fails, the error is
annotated with that context.AWS_METADATA_URL) are still read, with a warning naming the replacement.
Renaming an env var without a migration warning breaks users silently.Config carries dozens of fields (proxies, CA bundles, retry modes, IMDS
toggles, account-ID allow/deny lists), and the behavioral spec lives in a
very large test suite exercising every precedence branch. Do not start
here — grow toward it.
A recently built provider for an appliance-style API (IBM Power HMC) needed
exactly three sources — provider block, environment variables, and a YAML
credentials file with named profiles — and implemented the chain by hand in
a self-contained internal/credentials package, the same shape as
credential-chain.md. Its design choices, generalized:
Provider interface + sentinel error. Retrieve(ctx)
returns the credentials or ErrNoCredentials to mean “fall through”. An
aggregate ChainError records every source tried and implements
Is(ErrNoCredentials) so the provider’s Configure can pick between
“here is how to supply credentials” and “your file is broken” with a
single errors.Is.host and insecure
each independently follow config > env > file profile > default. A profile
can therefore hold only connection settings while credentials come from
the environment.String()/GoString() print ***REDACTED*** for the secret, making the
value log-safe by construction.chmod 0600 warning; disabling TLS verification produces a
warning naming the risk.getenv function and temp-dir
credential files make precedence tests (StaticWins, EnvBeatsFile,
FallsThroughToFile), aggregate-error tests, and redaction tests run
without touching the real environment.| Your situation | Chain to build |
|---|---|
| Single API token, no files | Static + env. Two providers, still worth the chain for its error aggregation |
| Human operators, multiple accounts | Add a credentials file with profiles (case study 2) |
| Runs inside the platform it manages | Add a platform-identity source (metadata/OIDC) at the end |
| Delegation/role semantics in the API | Add assume-role wrapping on top of the chain (case study 1) |
Whatever the size: keep the order in one constructor, aggregate every
skipped source into the failure message, and validate eagerly in
Configure.