This is an automated email from the ASF dual-hosted git repository.

laskoviymishka pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new e303dfde feat(io): add ManagedIdentityCredential support for Azure 
(#1062)
e303dfde is described below

commit e303dfdeb8fc51834573cc7f6ccff1c454478744
Author: Alessandro Nori <[email protected]>
AuthorDate: Tue May 26 14:29:42 2026 +0200

    feat(io): add ManagedIdentityCredential support for Azure (#1062)
    
    ## Changes
    - Add `ADLSManagedIdentityEnabled`
    (`adls.auth.managed-identity.enabled`) property to opt into
    `ManagedIdentityCredential` instead of `DefaultAzureCredential`
    - Add `ADLSManagedIdentityClientID`
    (`adls.auth.managed-identity.client-id`) property for user-assigned
    managed identities
    
    ## Motivation
    `DefaultAzureCredential` sets a short timeout on its first managed
    identity attempt. In production, this causes frequent "managed identity
    timed out" errors when the app requests a token. Using
    `ManagedIdentityCredential` directly avoids this timeout.
    
    Reference:
    
https://github.com/Azure/azure-sdk-for-go/blob/main/sdk/azidentity/TROUBLESHOOTING.md#troubleshoot-defaultazurecredential-authentication-issues
---
 io/config.go             |  2 ++
 io/gocloud/azure.go      | 22 ++++++++++++++++++++++
 io/gocloud/azure_test.go | 20 ++++++++++++++++++++
 3 files changed, 44 insertions(+)

diff --git a/io/config.go b/io/config.go
index 712c8d0d..c66b0c3d 100644
--- a/io/config.go
+++ b/io/config.go
@@ -47,8 +47,10 @@ const (
        ADLSConnectionStringPrefix = "adls.connection-string."
        ADLSSharedKeyAccountName   = "adls.auth.shared-key.account.name"
        ADLSSharedKeyAccountKey    = "adls.auth.shared-key.account.key"
+       ADLSClientID               = "adls.client-id"
        ADLSEndpoint               = "adls.endpoint"
        ADLSProtocol               = "adls.protocol"
+       ADLSManagedIdentityEnabled = "adls.auth.managed-identity.enabled"
 
        // Not in use yet
        // ADLSReadBlockSize          = "adls.read.block-size-bytes"
diff --git a/io/gocloud/azure.go b/io/gocloud/azure.go
index 06da2e2c..64177f14 100644
--- a/io/gocloud/azure.go
+++ b/io/gocloud/azure.go
@@ -159,6 +159,28 @@ func createAzureBucket(ctx context.Context, parsed 
*url.URL, props map[string]st
                if err != nil {
                        return nil, fmt.Errorf("failed 
container.NewClientFromConnectionString: %w", err)
                }
+       } else if props[io.ADLSManagedIdentityEnabled] == "true" {
+               containerURL, err := createContainerURL(location.accountName, 
protocol, endpoint, "", location.containerName)
+               if err != nil {
+                       return nil, err
+               }
+
+               var miOpts *azidentity.ManagedIdentityCredentialOptions
+               if clientID := props[io.ADLSClientID]; clientID != "" {
+                       miOpts = &azidentity.ManagedIdentityCredentialOptions{
+                               ID: azidentity.ClientID(clientID),
+                       }
+               }
+
+               cred, err := azidentity.NewManagedIdentityCredential(miOpts)
+               if err != nil {
+                       return nil, fmt.Errorf("failed 
azidentity.NewManagedIdentityCredential: %w", err)
+               }
+
+               client, err = container.NewClient(containerURL, cred, nil)
+               if err != nil {
+                       return nil, fmt.Errorf("failed container.NewClient: 
%w", err)
+               }
        } else {
                containerURL, err := createContainerURL(location.accountName, 
protocol, endpoint, "", location.containerName)
                if err != nil {
diff --git a/io/gocloud/azure_test.go b/io/gocloud/azure_test.go
index cb1404e0..d5ef7e40 100644
--- a/io/gocloud/azure_test.go
+++ b/io/gocloud/azure_test.go
@@ -70,6 +70,26 @@ func TestCreateAzureBucketDefaultCredentialEmptyBucketName(t 
*testing.T) {
                "Expected container name error but got: %v", err)
 }
 
+func TestCreateAzureBucketManagedIdentityCredentialCalled(t *testing.T) {
+       ctx := context.Background()
+
+       parsedURL, err := 
url.Parse("abfs://[email protected]/path")
+       assert.NoError(t, err)
+
+       // NewManagedIdentityCredential never fails at construction, so bucket 
creation always succeeds.
+       bucket, err := createAzureBucket(ctx, parsedURL, map[string]string{
+               "adls.auth.managed-identity.enabled": "true",
+       })
+       assert.NoError(t, err)
+       assert.NotNil(t, bucket)
+
+       iter := bucket.List(nil)
+       _, err = iter.Next(ctx)
+       assert.Error(t, err)
+       assert.Contains(t, err.Error(), "ManagedIdentityCredential",
+               "Expected ManagedIdentityCredential error but got: %v", err)
+}
+
 func TestCreateAzureBucketSharedKeyMissingAccountKey(t *testing.T) {
        ctx := context.Background()
 

Reply via email to