{"record":{"id":"8cb7620034251d9d","repo":"apache/druid","slug":"set-only-one-of-key-or-sharedaccessstoragetoken","errorCode":null,"errorMessage":"Set only one of 'key' or 'sharedAccessStorageToken' or 'useAzureCredentialsChain' in the azure config. Please refer to azure documentation.","messagePattern":"Set only one of 'key' or 'sharedAccessStorageToken' or 'useAzureCredentialsChain' in the azure config\\. Please refer to azure documentation\\.","errorType":"validation","errorClass":"ISE","httpStatus":null,"severity":"error","filePath":"extensions-core/azure-extensions/src/main/java/org/apache/druid/storage/azure/AzureStorageDruidModule.java","lineNumber":135,"sourceCode":"    if (StringUtils.isEmpty(config.getAccount())) {\n      throw new ISE(\"Set 'account' to the storage account that needs to be configured in the azure config.\"\n          + \" Please refer to azure documentation.\");\n    }\n\n    if (StringUtils.isEmpty(config.getKey()) && StringUtils.isEmpty(config.getSharedAccessStorageToken()) && BooleanUtils.isNotTrue(config.getUseAzureCredentialsChain())) {\n      throw new ISE(\"Either set 'key' or 'sharedAccessStorageToken' or 'useAzureCredentialsChain' in the azure config.\"\n          + \" Please refer to azure documentation.\");\n    }\n\n    /* Azure named keys and sas tokens are mutually exclusive with each other and with azure keychain auth,\n    but any form of auth supported by the DefaultAzureCredentialChain is not mutually exclusive, e.g. you can have\n    environment credentials or workload credentials or managed credentials using the same chain.\n    **/\n    if (!StringUtils.isEmpty(config.getKey()) && !StringUtils.isEmpty(config.getSharedAccessStorageToken()) ||\n        !StringUtils.isEmpty(config.getKey()) && BooleanUtils.isTrue(config.getUseAzureCredentialsChain()) ||\n        !StringUtils.isEmpty(config.getSharedAccessStorageToken()) && BooleanUtils.isTrue(config.getUseAzureCredentialsChain())\n    ) {\n      throw new ISE(\"Set only one of 'key' or 'sharedAccessStorageToken' or 'useAzureCredentialsChain' in the azure config.\"\n          + \" Please refer to azure documentation.\");\n    }\n    return new AzureClientFactory(config);\n  }\n\n  @Provides\n  @Global\n  @LazySingleton\n  public AzureStorage getAzureStorageContainer(\n      final AzureClientFactory azureClientFactory,\n      final AzureAccountConfig azureAccountConfig\n  )\n  {\n    return new AzureStorage(azureClientFactory, azureAccountConfig.getAccount());\n  }\n}\n","sourceCodeStart":117,"sourceCodeEnd":152,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/extensions-core/azure-extensions/src/main/java/org/apache/druid/storage/azure/AzureStorageDruidModule.java#L117-L152","documentation":"This IllegalStateException is thrown at Azure extension startup (AzureClientFactory provider) when the Azure storage configuration defines more than one mutually exclusive authentication mechanism. The deep storage can authenticate with exactly one of: a storage account key ('key'), a shared access storage token ('sharedAccessStorageToken'), or the Azure credentials chain ('useAzureCredentialsChain'). Supplying two or more is ambiguous and rejected immediately during DI initialization.","triggerScenarios":"Setting both azure.key and azure.sharedAccessStorageToken, or azure.key with azure.useAzureCredentialsChain=true, or azure.sharedAccessStorageToken with azure.useAzureCredentialsChain=true, in the druid.storage.azure config when the AzureStorageDruidModule provider getAzureClientFactory is invoked.","commonSituations":"Operators migrating from key-based auth to the credentials chain (managed identity / workload identity) who add useAzureCredentialsChain without removing the old key; copy-pasted config snippets that include both a SAS token and an account key; environment-specific config overlays that each set a different credential field, which merge into a combined config.","solutions":["Inspect the azure config (druid.storage.* properties) and keep exactly one of 'key', 'sharedAccessStorageToken', or 'useAzureCredentialsChain'; remove or blank the others","To switch to managed/workload identity, set azure.useAzureCredentialsChain=true and delete azure.key and azure.sharedAccessStorageToken","To keep static credentials, set exactly one of azure.key or azure.sharedAccessStorageToken and leave useAzureCredentialsChain unset or false","Restart the Druid service and confirm the AzureClientFactory initializes without the ISE"],"exampleFix":"// before\n{\n  \"druid.storage.azure.account\": \"myaccount\",\n  \"druid.storage.azure.key\": \"<account-key>\",\n  \"druid.storage.azure.useAzureCredentialsChain\": true\n}\n// after\n{\n  \"druid.storage.azure.account\": \"myaccount\",\n  \"druid.storage.azure.useAzureCredentialsChain\": true\n}","handlingStrategy":"validation","validationCode":"int set = (isEmpty(config.getKey())?0:1) + (isEmpty(config.getSharedAccessStorageToken())?0:1) + (Boolean.TRUE.equals(config.getUseAzureCredentialsChain())?1:0);\nif (set > 1) throw new IllegalArgumentException(\"Set only one of 'key', 'sharedAccessStorageToken', or 'useAzureCredentialsChain'\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep one canonical auth block per environment and template it (key XOR SAS XOR credentials-chain)","When enabling useAzureCredentialsChain, remove the key/SAS fields in the same config change","Add a config-lint step in CI that checks the Azure storage properties for mutual exclusivity"],"tags":["configuration","azure","startup","mutually-exclusive"],"backgroundTag":"mutually-exclusive-flags","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}