Skip to content

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.