Share one credential across components¶
You are using more than one Azure-backed component — configuration from Key Vault, more from App Configuration, files from Blob Storage — and you want one identity resolution between them.
Build one source, inject it everywhere¶
import (
"gitlab.com/phpboyscout/go/azureclient"
"gitlab.com/phpboyscout/go/config"
acambient "gitlab.com/phpboyscout/go/config-azure-appconfig/ambient"
kvambient "gitlab.com/phpboyscout/go/config-azure-keyvault/ambient"
)
src := azureclient.Ambient(azureclient.WithTenantID(tenantID))
vault, err := kvambient.FromSource(ctx, src, "https://my-vault.vault.azure.net/")
if err != nil {
return err
}
settings, err := acambient.FromSource(ctx, src, "https://my-store.azconfig.io")
if err != nil {
return err
}
store, err := config.NewStore(ctx,
config.WithBackend(vault),
config.WithBackend(settings),
)
One identity resolution, two adapters.
Note the import paths: FromSource lives in each adapter's ambient
subpackage, not its root. That boundary is deliberate — the root package must not
carry the Azure identity graph, which measures at +7 modules. See
the footprint boundary.
What you are trading away¶
Without this, each adapter resolves its own identity. That is the default and it is deliberate: components stay isolated, and one component's transient failure is not everybody's.
Sharing gives you one resolution and one failure domain. They arrive together, so choose it because you want both.
Connection strings are a different rung¶
config-azure-appconfig and config-azure-blob also accept a connection string,
which carries the endpoint and the secret together. That rung does not involve a
TokenCredential at all, so this module has nothing to do with it.
Worth knowing: a connection string may embed a SAS with an expiry, and nothing renews it. Because you supply it, the secret is already in your hands — but if your process outlives the SAS, that is where it will fail.
Bounding and scoping¶
src := azureclient.Ambient(
azureclient.WithBuildTimeout(5*time.Second),
azureclient.WithLifetimeContext(appCtx),
)
WithBuildTimeout bounds one attempt, not a total budget, and is cooperative —
it cancels the context the SDK is given. WithLifetimeContext ties attempts to
the life of whatever owns the source; it is deliberately not a call's context.