Skip to content

Commit d2e7472

Browse files
authored
DefaultAzureCredential reads AZURE_CLIENT_ID for user-assigned managed identities (Azure#17293)
1 parent fd2317b commit d2e7472

File tree

7 files changed

+55
-18
lines changed

7 files changed

+55
-18
lines changed

sdk/azidentity/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
## 0.15.0 (Unreleased)
44

55
### Features Added
6+
* `DefaultAzureCredential` reads environment variable `AZURE_CLIENT_ID` for the
7+
client ID of a user-assigned managed identity
8+
([#17293](https://github.com/Azure/azure-sdk-for-go/pull/17293))
69

710
### Breaking Changes
811
* Removed `AuthorizationCodeCredential`. Use `InteractiveBrowserCredential` instead

sdk/azidentity/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ in any hosting environment which supports managed identities, such as (this list
7676

7777
- [Authenticate with DefaultAzureCredential](#authenticate-with-defaultazurecredential "Authenticate with DefaultAzureCredential")
7878
- [Define a custom authentication flow with ChainedTokenCredential](#define-a-custom-authentication-flow-with-chainedtokencredential "Define a custom authentication flow with ChainedTokenCredential")
79+
- [Specify a user-assigned managed identity for DefaultAzureCredential](#specify-a-user-assigned-managed-identity-for-defaultazurecredential)
7980

8081
### Authenticate with DefaultAzureCredential
8182

@@ -90,6 +91,9 @@ if err != nil {
9091
client := armresources.NewResourceGroupsClient("subscription ID", cred, nil)
9192
```
9293

94+
### Specify a user-assigned managed identity for DefaultAzureCredential
95+
96+
To configure `DefaultAzureCredential` to authenticate a user-assigned managed identity, set the environment variable `AZURE_CLIENT_ID` to the identity's client ID.
9397

9498
### Define a custom authentication flow with `ChainedTokenCredential`
9599

sdk/azidentity/azidentity.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ import (
2323
)
2424

2525
const (
26+
azureAuthorityHost = "AZURE_AUTHORITY_HOST"
27+
azureClientID = "AZURE_CLIENT_ID"
28+
2629
organizationsTenantID = "organizations"
2730
developerSignOnClientID = "04b07795-8ddb-461a-bbee-02f9e1bf7b46"
2831
defaultSuffix = "/.default"
2932
tenantIDValidationErr = "invalid tenantID. You can locate your tenantID by following the instructions listed here: https://docs.microsoft.com/partner-center/find-ids-and-domain-names"
3033
)
3134

32-
const azureAuthorityHost = "AZURE_AUTHORITY_HOST"
33-
3435
// setAuthorityHost initializes the authority host for credentials. Precedence is:
3536
// 1. cloud.Configuration.LoginEndpoint value set by user
3637
// 2. value of AZURE_AUTHORITY_HOST

sdk/azidentity/default_azure_credential.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package azidentity
66
import (
77
"context"
88
"errors"
9+
"os"
910
"strings"
1011
"time"
1112

@@ -52,7 +53,11 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default
5253
creds = append(creds, &defaultCredentialErrorReporter{credType: "EnvironmentCredential", err: err})
5354
}
5455

55-
msiCred, err := NewManagedIdentityCredential(&ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions})
56+
o := &ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions}
57+
if ID, ok := os.LookupEnv(azureClientID); ok {
58+
o.ID = ClientID(ID)
59+
}
60+
msiCred, err := NewManagedIdentityCredential(o)
5661
if err == nil {
5762
creds = append(creds, msiCred)
5863
msiCred.client.imdsTimeout = time.Second

sdk/azidentity/default_azure_credential_test.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ package azidentity
55

66
import (
77
"context"
8+
"fmt"
89
"testing"
910

1011
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
1112
"github.com/Azure/azure-sdk-for-go/sdk/internal/log"
1213
)
1314

1415
func TestDefaultAzureCredential_GetTokenSuccess(t *testing.T) {
15-
env := map[string]string{"AZURE_TENANT_ID": fakeTenantID, "AZURE_CLIENT_ID": fakeClientID, "AZURE_CLIENT_SECRET": secret}
16+
env := map[string]string{"AZURE_TENANT_ID": fakeTenantID, azureClientID: fakeClientID, "AZURE_CLIENT_SECRET": secret}
1617
setEnvironmentVariables(t, env)
1718
cred, err := NewDefaultAzureCredential(nil)
1819
if err != nil {
@@ -62,3 +63,26 @@ func TestDefaultAzureCredential_ConstructorErrorHandler(t *testing.T) {
6263
t.Fatalf("Did not receive the expected logs.\n\nReceived:\n%s\n\nExpected:\n%s", logMessages[0], expectedLogs)
6364
}
6465
}
66+
67+
func TestDefaultAzureCredential_UserAssignedIdentity(t *testing.T) {
68+
for _, ID := range []ManagedIDKind{nil, ClientID("client-id")} {
69+
t.Run(fmt.Sprintf("%v", ID), func(t *testing.T) {
70+
if ID != nil {
71+
t.Setenv(azureClientID, ID.String())
72+
}
73+
cred, err := NewDefaultAzureCredential(nil)
74+
if err != nil {
75+
t.Fatal(err)
76+
}
77+
for _, c := range cred.chain.sources {
78+
if mic, ok := c.(*ManagedIdentityCredential); ok {
79+
if mic.id != ID {
80+
t.Fatalf(`expected %v, got "%v"`, ID, mic.id)
81+
}
82+
return
83+
}
84+
}
85+
t.Fatal("default chain should include ManagedIdentityCredential")
86+
})
87+
}
88+
}

sdk/azidentity/environment_credential.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ func NewEnvironmentCredential(options *EnvironmentCredentialOptions) (*Environme
5656
if tenantID == "" {
5757
return nil, errors.New("missing environment variable AZURE_TENANT_ID")
5858
}
59-
clientID := os.Getenv("AZURE_CLIENT_ID")
59+
clientID := os.Getenv(azureClientID)
6060
if clientID == "" {
61-
return nil, errors.New("missing environment variable AZURE_CLIENT_ID")
61+
return nil, errors.New("missing environment variable " + azureClientID)
6262
}
6363
if clientSecret := os.Getenv("AZURE_CLIENT_SECRET"); clientSecret != "" {
6464
log.Write(EventAuthentication, "EnvironmentCredential will authenticate with ClientSecretCredential")

sdk/azidentity/environment_credential_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ import (
1515
)
1616

1717
func resetEnvironmentVarsForTest() {
18-
clearEnvVars("AZURE_TENANT_ID", "AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_CLIENT_CERTIFICATE_PATH", "AZURE_USERNAME", "AZURE_PASSWORD")
18+
clearEnvVars("AZURE_TENANT_ID", azureClientID, "AZURE_CLIENT_SECRET", "AZURE_CLIENT_CERTIFICATE_PATH", "AZURE_USERNAME", "AZURE_PASSWORD")
1919
}
2020

2121
func TestEnvironmentCredential_TenantIDNotSet(t *testing.T) {
2222
resetEnvironmentVarsForTest()
23-
err := os.Setenv("AZURE_CLIENT_ID", fakeClientID)
23+
err := os.Setenv(azureClientID, fakeClientID)
2424
if err != nil {
2525
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
2626
}
@@ -56,7 +56,7 @@ func TestEnvironmentCredential_ClientSecretNotSet(t *testing.T) {
5656
if err != nil {
5757
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
5858
}
59-
err = os.Setenv("AZURE_CLIENT_ID", fakeClientID)
59+
err = os.Setenv(azureClientID, fakeClientID)
6060
if err != nil {
6161
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
6262
}
@@ -72,7 +72,7 @@ func TestEnvironmentCredential_ClientSecretSet(t *testing.T) {
7272
if err != nil {
7373
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
7474
}
75-
err = os.Setenv("AZURE_CLIENT_ID", fakeClientID)
75+
err = os.Setenv(azureClientID, fakeClientID)
7676
if err != nil {
7777
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
7878
}
@@ -95,7 +95,7 @@ func TestEnvironmentCredential_ClientCertificatePathSet(t *testing.T) {
9595
if err != nil {
9696
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
9797
}
98-
err = os.Setenv("AZURE_CLIENT_ID", fakeClientID)
98+
err = os.Setenv(azureClientID, fakeClientID)
9999
if err != nil {
100100
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
101101
}
@@ -118,7 +118,7 @@ func TestEnvironmentCredential_UsernameOnlySet(t *testing.T) {
118118
if err != nil {
119119
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
120120
}
121-
err = os.Setenv("AZURE_CLIENT_ID", fakeClientID)
121+
err = os.Setenv(azureClientID, fakeClientID)
122122
if err != nil {
123123
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
124124
}
@@ -138,7 +138,7 @@ func TestEnvironmentCredential_UsernamePasswordSet(t *testing.T) {
138138
if err != nil {
139139
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
140140
}
141-
err = os.Setenv("AZURE_CLIENT_ID", fakeClientID)
141+
err = os.Setenv(azureClientID, fakeClientID)
142142
if err != nil {
143143
t.Fatalf("Unexpected error when initializing environment variables: %v", err)
144144
}
@@ -169,7 +169,7 @@ func TestEnvironmentCredential_SendCertificateChain(t *testing.T) {
169169
srv.AppendResponse()
170170

171171
vars := map[string]string{
172-
"AZURE_CLIENT_ID": liveSP.clientID,
172+
azureClientID: liveSP.clientID,
173173
"AZURE_CLIENT_CERTIFICATE_PATH": liveSP.pfxPath,
174174
"AZURE_TENANT_ID": liveSP.tenantID,
175175
envVarSendCertChain: "true",
@@ -190,7 +190,7 @@ func TestEnvironmentCredential_SendCertificateChain(t *testing.T) {
190190

191191
func TestEnvironmentCredential_ClientSecretLive(t *testing.T) {
192192
vars := map[string]string{
193-
"AZURE_CLIENT_ID": liveSP.clientID,
193+
azureClientID: liveSP.clientID,
194194
"AZURE_CLIENT_SECRET": liveSP.secret,
195195
"AZURE_TENANT_ID": liveSP.tenantID,
196196
}
@@ -206,7 +206,7 @@ func TestEnvironmentCredential_ClientSecretLive(t *testing.T) {
206206

207207
func TestEnvironmentCredential_InvalidClientSecretLive(t *testing.T) {
208208
vars := map[string]string{
209-
"AZURE_CLIENT_ID": liveSP.clientID,
209+
azureClientID: liveSP.clientID,
210210
"AZURE_CLIENT_SECRET": "invalid secret",
211211
"AZURE_TENANT_ID": liveSP.tenantID,
212212
}
@@ -232,7 +232,7 @@ func TestEnvironmentCredential_InvalidClientSecretLive(t *testing.T) {
232232

233233
func TestEnvironmentCredential_UserPasswordLive(t *testing.T) {
234234
vars := map[string]string{
235-
"AZURE_CLIENT_ID": developerSignOnClientID,
235+
azureClientID: developerSignOnClientID,
236236
"AZURE_TENANT_ID": liveUser.tenantID,
237237
"AZURE_USERNAME": liveUser.username,
238238
"AZURE_PASSWORD": liveUser.password,
@@ -249,7 +249,7 @@ func TestEnvironmentCredential_UserPasswordLive(t *testing.T) {
249249

250250
func TestEnvironmentCredential_InvalidPasswordLive(t *testing.T) {
251251
vars := map[string]string{
252-
"AZURE_CLIENT_ID": developerSignOnClientID,
252+
azureClientID: developerSignOnClientID,
253253
"AZURE_TENANT_ID": liveUser.tenantID,
254254
"AZURE_USERNAME": liveUser.username,
255255
"AZURE_PASSWORD": "invalid password",

0 commit comments

Comments
 (0)