Skip to content

Commit d07625c

Browse files
authored
Feat support for managed identity authentication in azure locking (#1896)
1 parent fdb01cc commit d07625c

File tree

3 files changed

+65
-9
lines changed

3 files changed

+65
-9
lines changed

docs/ce/azure-specific/azure-devops-locking-connection-methods.mdx

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,22 @@ There is one mandatory environment variable the user will have to set, in order
1212

1313
* CLIENT\_SECRET
1414

15+
* MANAGED\_IDENTITY
16+
1517
Then, depending on the value of `DIGGER_AZURE_AUTH_METHOD`, the user will have to set other environment variables.
1618

17-
1. **SHARED\_KEY**
18-
* `DIGGER_AZURE_SA_NAME`: Storage account name
19+
1. **SHARED\_KEY**
20+
* `DIGGER_AZURE_SA_NAME`: Storage account name
1921
* `DIGGER_AZURE_SHARED_KEY`: shared key of the storage account
2022

21-
2. **CONNECTION\_STRING**
23+
2. **CONNECTION\_STRING**
2224
* `DIGGER_AZURE_CONNECTION_STRING`: connection string
2325

24-
3. **CLIENT\_SECRET**
25-
* `DIGGER_AZURE_TENANT_ID`: tenant id to use
26-
* `DIGGER_AZURE_CLIENT_ID`: client id of the service principal
27-
* `DIGGER_AZURE_CLIENT_SECRET`: secret of the service principal
28-
* `DIGGER_AZURE_SA_NAME`: storage account name
26+
3. **CLIENT\_SECRET**
27+
* `DIGGER_AZURE_TENANT_ID`: tenant id to use
28+
* `DIGGER_AZURE_CLIENT_ID`: client id of the service principal
29+
* `DIGGER_AZURE_CLIENT_SECRET`: secret of the service principal
30+
* `DIGGER_AZURE_SA_NAME`: storage account name
31+
32+
4. **MANAGED\_IDENTITY**
33+
* `DIGGER_AZURE_SA_NAME`: storage account name

libs/locking/azure/storage_account.go

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type StorageAccount struct {
2727
func NewStorageAccountLock() (*StorageAccount, error) {
2828
authMethod := os.Getenv("DIGGER_AZURE_AUTH_METHOD")
2929
if authMethod == "" {
30-
return nil, fmt.Errorf("'DIGGER_AZURE_AUTH_METHOD' environment variable must be set to either 'SHARED_KEY' or 'CONNECTION_STRING' or 'CLIENT_SECRET'")
30+
return nil, fmt.Errorf("'DIGGER_AZURE_AUTH_METHOD' environment variable must be set to either 'SHARED_KEY' or 'CONNECTION_STRING' or 'CLIENT_SECRET' or 'MANAGED_IDENTITY")
3131
}
3232

3333
svcClient, err := getServiceClient(authMethod)
@@ -128,6 +128,10 @@ func getServiceClient(authMethod string) (*aztables.ServiceClient, error) {
128128
return getClientSecretSvcClient()
129129
}
130130

131+
if authMethod == "MANAGED_IDENTITY" {
132+
return getManagedIdentitySvcCLient()
133+
}
134+
131135
return nil, fmt.Errorf("could not initialize service client, because no valid authentication method was found")
132136
}
133137

@@ -187,6 +191,27 @@ func getClientSecretSvcClient() (*aztables.ServiceClient, error) {
187191
return svcClient, nil
188192
}
189193

194+
func getManagedIdentitySvcCLient() (*aztables.ServiceClient, error) {
195+
saName := os.Getenv("DIGGER_AZURE_SA_NAME")
196+
197+
if saName == "" {
198+
return nil, fmt.Errorf("you must set 'DIGGER_AZURE_SA_NAME' when using managed identity authentication")
199+
}
200+
201+
serviceURL := getServiceURL(saName)
202+
203+
cred, err := azidentity.NewManagedIdentityCredential(nil)
204+
if err != nil {
205+
return nil, fmt.Errorf("could not create create managed identity credential: %v", err)
206+
}
207+
208+
svcClient, err := aztables.NewServiceClient(serviceURL, cred, nil)
209+
if err != nil {
210+
return nil, fmt.Errorf("could not create service client with managed identity authentication: %v", err)
211+
}
212+
return svcClient, nil
213+
}
214+
190215
func (sal *StorageAccount) createTableIfNotExists() error {
191216
exists, err := sal.isTableExists(TABLE_NAME)
192217
if err != nil {

libs/locking/azure/storage_account_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,17 @@ var (
6464
loadClientSecretEnv()
6565
},
6666
},
67+
{
68+
name: "Managed Identity authentication mode",
69+
loadEnv: func(s *SALockTestSuite) {
70+
// Skip this test case if we are not testing on a real storage account
71+
if !usingRealSA {
72+
s.T().Skip("Managed identity method can only be tested when used against a real storage account.")
73+
return
74+
}
75+
loadManagedIdentityEnv()
76+
},
77+
},
6778
}
6879
)
6980

@@ -137,6 +148,15 @@ func (suite *SALockTestSuite) TestNewStorageAccountLock_WithClientSecret_Missing
137148
suite.Error(err, "should have got an error")
138149
}
139150

151+
func (suite *SALockTestSuite) TestNewStorageAccountLock_WithManagedIdentity_MissingEnv() {
152+
loadManagedIdentityEnv()
153+
os.Setenv("DIGGER_AZURE_SA_NAME", "")
154+
155+
sal, err := NewStorageAccountLock()
156+
suite.Nil(sal)
157+
suite.Error(err, "should have got an error")
158+
}
159+
140160
func (suite *SALockTestSuite) TestLock_WhenNotLockedYet() {
141161
for _, tc := range testCases {
142162
suite.Run(tc.name, func() {
@@ -373,6 +393,12 @@ func loadClientSecretEnv() {
373393
os.Setenv("DIGGER_AZURE_SA_NAME", envs["DIGGER_AZURE_SA_NAME"])
374394
}
375395

396+
func loadManagedIdentityEnv() {
397+
os.Setenv("DIGGER_AZURE_AUTH_METHOD", "MANAGED_IDENTITY")
398+
399+
os.Setenv("DIGGER_AZURE_SA_NAME", envs["DIGGER_AZURE_SA_NAME"])
400+
}
401+
376402
// Clean environment variables
377403
func cleanEnv() {
378404
for _, env := range envNames {

0 commit comments

Comments
 (0)