hashicorp/terraform · error
retrieving key for Storage Account
Error message
retrieving key for Storage Account %q: %s
What it means
Thrown by getBlobClient in the default branch (no access_key, no sas, no aad) when c.accountDetail.AccountKey(ctx, storageAccountsClient) fails. AccountKey calls the ARM storageAccounts.ListKeys API and extracts a Full-permission key; failure means the ARM call failed (permissions, network, throttling) or no Full-permission key was returned.
Solutions
- Grant the principal Storage Account Contributor (or the narrower Storage Account Key Operator Service role) on the account.
- Switch to use_azuread_auth = true (recommended modern path) to avoid the key-list entirely.
- Provide access_key directly.
- Retry for transient ARM throttling; check Azure status for region incidents.
Defensive patterns
Strategy: validation
Validate before calling
// Pre-flight: verify the principal can list storage keys before configuring the backend.
// (Requires az login or an authenticated SDK client.)
func canListKeys(ctx context.Context, sub, rg, acct string) error {
// use az CLI as the simplest check
cmd := exec.CommandContext(ctx, "az", "storage", "account", "keys", "list", "-g", rg, "-n", acct, "--query", "[0].value", "-o", "tsv")
out, err := cmd.Output()
if err != nil { return fmt.Errorf("cannot list keys: %w (stderr: %s)", err, out) }
if len(strings.TrimSpace(string(out))) == 0 { return fmt.Errorf("listed key was empty") }
return nil
} Try / catch
// If you must call getBlobClient directly, retry once on the list-keys failure (transient throttling).
var blob *blobs.Client
err := backoff.Retry(func() error {
b, err := apiClient.GetBlobClient(ctx)
if err != nil && strings.Contains(err.Error(), "retrieving key for Storage Account") {
return err // retryable
}
if err != nil { return backoff.Permanent(err) }
blob = b
return nil
}, backoff.WithMaxRetries(backoff.NewExponentialBackOff(), 3)) Prevention
- Grant the principal Storage Account Key Operator Service (least privilege) or Storage Account Contributor.
- Use use_azuread_auth = true to sidestep the listKeys path.
- In CI, run a `az role assignment list` smoke test before terraform init.
When it happens
Trigger: Default auth path (CLI/principal with no explicit data-plane creds). (a) The identity lacks Microsoft.Storage/storageAccounts/listKeys/action. (b) ListKeys returned keys but none has KeyPermission == Full. (c) ARM throttled or network failed.
Common situations: Service principal or managed identity with only Reader / Contributor-via-other-scope role; cross-tenant access where the principal cannot list keys; subscription throttling on busy accounts; key regeneration left the account in a transitional state.
Related errors
- retrieving : %+v
- new shared key authorizer
- populating details for
- retrieving : model was nil
- unable to build authorizer for Resource Manager API: %+v
AI-assisted analysis of hashicorp/terraform@d32a084675 (2026-08-11).
Data as JSON: /api/errors/ea90b0c6b6bbc831.
Report an issue: GitHub.
Appendix: source
Thrown at internal/backend/remote-state/azure/api_client.go:184
log.Printf("[DEBUG] Building the Blob Client from an Access Key")
authorizer, err := auth.NewSharedKeyAuthorizer(c.storageAccountName, c.accessKey, auth.SharedKey)
if err != nil {
return nil, fmt.Errorf("new shared key authorizer: %v", err)
}
c.configureClient(blobsClient.Client, authorizer)
return blobsClient, nil
case c.azureAdStorageAuth != nil:
log.Printf("[DEBUG] Building the Blob Client from AAD auth")
c.configureClient(blobsClient.Client, c.azureAdStorageAuth)
return blobsClient, nil
default:
// Neither shared access key, sas token, or AAD Auth were specified so we have to call the management plane API to get the key.
log.Printf("[DEBUG] Building the Blob Client from an Access Key (key is listed using client credentials)")
key, err := c.accountDetail.AccountKey(ctx, c.storageAccountsClient)
if err != nil {
return nil, fmt.Errorf("retrieving key for Storage Account %q: %s", c.storageAccountName, err)
}
authorizer, err := auth.NewSharedKeyAuthorizer(c.storageAccountName, *key, auth.SharedKey)
if err != nil {
return nil, fmt.Errorf("new shared key authorizer: %v", err)
}
c.configureClient(blobsClient.Client, authorizer)
return blobsClient, nil
}
}
func (c *Client) getContainersClient(ctx context.Context) (cc *containers.Client, err error) {
if c.containersClient != nil {
return c.containersClient, nil
}
defer func() {
if err == nil {
c.containersClient = ccView on GitHub (pinned to d32a084675)