hashicorp/terraform · error

unable to build authorizer for Storage API: %+v

Error message

unable to build authorizer for Storage API: %+v

What it means

Thrown by Azure buildClient in the use_azuread_authentication=true branch when auth.NewAuthorizerFromCredentials cannot construct an Azure AD authorizer for the Storage data-plane scope. The %+v expands the underlying auth error (missing field, bad endpoint, wrong tenant, etc.).

Solutions

  1. Read the wrapped %+v error first; it usually names the exact missing/invalid field.
  2. Confirm client_id, tenant_id (or tenant_id for OIDC), and client_secret / client_certificate_path / use_oidc are all set and consistent in the azurerm backend block or ARM_* env vars.
  3. Verify the environment name resolves a Storage endpoint (for custom clouds, define environment metadata with a Storage resource manager).
  4. Rotate an expired client secret and update both the app registration and the backend config.

Example fix

# before: missing tenant_id / wrong scope
export ARM_CLIENT_ID=00000000-0000-0000-0000-000000000000
export ARM_USE_AAD=true
# after
export ARM_CLIENT_ID=00000000-0000-0000-0000-000000000000
export ARM_CLIENT_SECRET=...
export ARM_TENANT_ID=11111111-1111-1111-1111-111111111111
export ARM_SUBSCRIPTION_ID=22222222-2222-2222-2222-222222222222
export ARM_USE_AAD=true
Defensive patterns

Strategy: validation

Validate before calling

func validateAADCreds(c BackendConfig) error {
    if c.AuthConfig == nil { return fmt.Errorf("missing auth config") }
    if c.AuthConfig.ClientID == "" { return fmt.Errorf("client_id missing for AAD auth") }
    if c.AuthConfig.TenantID == "" { return fmt.Errorf("tenant_id missing for AAD auth") }
    if !c.AuthConfig.UseOIDC && c.AuthConfig.ClientSecret == "" && c.AuthConfig.ClientCertificatePath == "" {
        return fmt.Errorf("client_secret or client_certificate_path required for non-OIDC AAD auth")
    }
    return nil
}

Type guard

null

Try / catch

client, err := azure.NewClient(ctx, cfg)
if err != nil && strings.Contains(err.Error(), "unable to build authorizer for Storage API") {
    // surface the wrapped %+v cause and point the user at client_id/tenant_id/secret
}

Prevention

When it happens

Trigger: config.UseAzureADAuthentication is true and auth.NewAuthorizerFromCredentials(ctx, *config.AuthConfig, config.AuthConfig.Environment.Storage) returns err. Typical root causes: client_id/tenant_id/client_secret missing or malformed, the named Environment does not define a Storage endpoint, or the credentials do not match a real app registration.

Common situations: Service principal misconfigured in the backend block or via ARM_* env vars (wrong tenant, expired client_secret, swapped client_id and object_id), using a custom cloud name that has no Storage endpoint, or AAD workarounds that omit required fields.

Related errors


AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11). Data as JSON: /api/errors/4dafaafc28c61651. Report an issue: GitHub.

Appendix: source

Thrown at internal/backend/remote-state/azure/api_client.go:64

		environment:        config.AuthConfig.Environment,
		storageAccountName: config.StorageAccountName,
	}

	var armAuthRequired bool
	switch {
	case config.AccessKey != "":
		client.accessKey = config.AccessKey
	case config.SasToken != "":
		sasToken := config.SasToken
		if strings.TrimSpace(sasToken) == "" {
			return nil, fmt.Errorf("sasToken cannot be empty")
		}
		client.sasToken = strings.TrimPrefix(sasToken, "?")
	case config.UseAzureADAuthentication:
		var err error
		client.azureAdStorageAuth, err = auth.NewAuthorizerFromCredentials(ctx, *config.AuthConfig, config.AuthConfig.Environment.Storage)
		if err != nil {
			return nil, fmt.Errorf("unable to build authorizer for Storage API: %+v", err)
		}
	default:
		// AAD authentication (ARM scope) is required only when no auth method is specified, which falls back to listing the access key via ARM API.
		armAuthRequired = true
	}

	// If `config.LookupBlobEndpoint` is true, we need to authenticate with ARM to lookup the blob endpoint
	if config.LookupBlobEndpoint {
		armAuthRequired = true
	}

	if armAuthRequired {
		resourceManagerAuth, err := auth.NewAuthorizerFromCredentials(ctx, *config.AuthConfig, config.AuthConfig.Environment.ResourceManager)
		if err != nil {
			return nil, fmt.Errorf("unable to build authorizer for Resource Manager API: %+v", err)
		}

		// When using Azure CLI to auth, the user can leave the "subscription_id" unspecified. In this case the subscription id is inferred from

View on GitHub (pinned to d32a084675)