Loading...
Loading...
Implement Terraform provider configuration and authentication with the Plugin Framework: provider schema for credentials (Optional + Sensitive attributes), environment variable fallbacks, credential provider chains (static config, then environment variables, shared credentials file, and platform identity), unknown-value guards in Configure(), secret redaction, configure-time credential validation, and diagnostics that name every source tried. Use when implementing or reviewing a provider's Configure method or provider schema, adding authentication options (API keys, tokens, profiles, credentials files, assume-role), deciding how a provider should resolve credentials, debugging "no valid credential sources" or missing-credentials errors, or unit testing credential resolution.
npx skill4agent add hashicorp/agent-skills provider-configurationexamplecloudreferences/credential-chain.mdreferences/case-studies.mdaws-sdk-go-baseOptionalRequiredRequiredSensitivetfplugindocsfunc (p *examplecloudProvider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
resp.Schema = schema.Schema{
Attributes: map[string]schema.Attribute{
"endpoint": schema.StringAttribute{
Optional: true,
MarkdownDescription: "API endpoint. May also be set via the `EXAMPLECLOUD_ENDPOINT` environment variable.",
},
"api_key": schema.StringAttribute{
Optional: true,
MarkdownDescription: "API key. May also be set via the `EXAMPLECLOUD_API_KEY` environment variable, or in a shared credentials file.",
},
"api_secret": schema.StringAttribute{
Optional: true,
Sensitive: true,
MarkdownDescription: "API secret. May also be set via the `EXAMPLECLOUD_API_SECRET` environment variable, or in a shared credentials file.",
},
"profile": schema.StringAttribute{
Optional: true,
MarkdownDescription: "Named profile in the shared credentials file. May also be set via the `EXAMPLECLOUD_PROFILE` environment variable. Defaults to `default`.",
},
"skip_credentials_validation": schema.BoolAttribute{
Optional: true,
MarkdownDescription: "Skip the identity check normally performed during provider configuration.",
},
},
}
}Defaultaws-sdk-go-baseproviderEXAMPLECLOUD_API_KEY~/.examplecloud/credentialsendpointprofileinsecure// ErrNoCredentials signals a source had nothing to offer. The chain falls
// through to the next source. Any other error means the source was
// configured but unusable (e.g. malformed credentials file) and is
// preserved so the final diagnostics can surface it.
var ErrNoCredentials = errors.New("no credentials found")
type Credentials struct {
APIKey string
APISecret string
Source string // which provider supplied them, for logging
}
func (c Credentials) Complete() bool {
return c.APIKey != "" && c.APISecret != ""
}
type Provider interface {
Retrieve(ctx context.Context) (Credentials, error)
Name() string
}ChainProviderChainErrorError()Iserrors.Is(err, ErrNoCredentials)ConfigureNewDefaultChainreferences/credential-chain.mdConfigurefunc (p *examplecloudProvider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
var config examplecloudProviderModel
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
if resp.Diagnostics.HasError() {
return
}
// 1. Guard against unknown values (e.g. api_key = some_resource.output).
if config.APIKey.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_key"),
"Unknown API Key",
"The provider cannot connect because api_key depends on a value known only after apply. "+
"Set a static value, or use the EXAMPLECLOUD_API_KEY environment variable.",
)
}
// ... repeat for each auth attribute, then:
if resp.Diagnostics.HasError() {
return
}
// 2. Resolve credentials through the chain.
chain := credentials.NewDefaultChain(
config.APIKey.ValueString(),
config.APISecret.ValueString(),
credentials.Options{Profile: config.Profile.ValueString()},
)
creds, err := chain.Retrieve(ctx)
if err != nil {
if errors.Is(err, credentials.ErrNoCredentials) {
resp.Diagnostics.AddError(
"No Valid Credential Sources Found",
"No examplecloud credentials were found. Sources tried, in order:\n\n"+err.Error()+
"\n\nSet api_key and api_secret in the provider block, export "+
"EXAMPLECLOUD_API_KEY and EXAMPLECLOUD_API_SECRET, or add a profile to "+
"~/.examplecloud/credentials. See https://example.com/docs/auth.",
)
} else {
resp.Diagnostics.AddError("Failed to Resolve Credentials", err.Error())
}
return
}
tflog.Debug(ctx, "resolved credentials", map[string]any{"source": creds.Source})
// 3. Build the client once; share it with every resource and data source.
client := examplecloud.NewClient(endpoint, creds.APIKey, creds.APISecret)
resp.DataSourceData = client
resp.ResourceData = client
}ChainErroraws-sdk-go-baseNoValidCredentialSourcesErrorprofilechmod 0600CredentialsString()GoString()%v%+vinfo.Mode().Perm()&0o077 != 0Configurests:GetCallerIdentity/whoamiskip_credentials_validationTestTF_ACCgetenv func(string) stringos.Getenvt.Setenvt.TempDir()ErrNoCredentialserrors.Is(err, ErrNoCredentials)fmt.Sprintf("%v")%+vCredentialsreferences/credential-chain.mdOptionalSensitive: trueConfigureErrNoCredentialsCredentialsString()GoString()Configureskip_credentials_validationnew-terraform-providerprovider-resources