diff --git a/eng/Packages.Data.props b/eng/Packages.Data.props
index df417b1aaa29..1b9da916ed13 100644
--- a/eng/Packages.Data.props
+++ b/eng/Packages.Data.props
@@ -120,7 +120,7 @@
-
+
@@ -261,7 +261,7 @@
-
+
diff --git a/sdk/extensions/Microsoft.Extensions.Azure/CHANGELOG.md b/sdk/extensions/Microsoft.Extensions.Azure/CHANGELOG.md
index 839e24420542..91af0e3de3a6 100644
--- a/sdk/extensions/Microsoft.Extensions.Azure/CHANGELOG.md
+++ b/sdk/extensions/Microsoft.Extensions.Azure/CHANGELOG.md
@@ -4,12 +4,16 @@
### Features Added
+- Added support for constructing a `ManagedIdentityCredential` from config by setting the `managedIdentityObjectId` key.
+
### Breaking Changes
### Bugs Fixed
### Other Changes
+- Updated dependency `Azure.Identity` to version `1.13.1`.
+
## 1.7.6 (2024-10-04)
### Other Changes
diff --git a/sdk/extensions/Microsoft.Extensions.Azure/src/Internal/ClientFactory.cs b/sdk/extensions/Microsoft.Extensions.Azure/src/Internal/ClientFactory.cs
index 0646bb2d3fcd..c0030882c893 100644
--- a/sdk/extensions/Microsoft.Extensions.Azure/src/Internal/ClientFactory.cs
+++ b/sdk/extensions/Microsoft.Extensions.Azure/src/Internal/ClientFactory.cs
@@ -97,6 +97,7 @@ internal static TokenCredential CreateCredential(IConfiguration configuration)
var clientId = configuration["clientId"];
var tenantId = configuration["tenantId"];
var resourceId = configuration["managedIdentityResourceId"];
+ var objectId = configuration["managedIdentityObjectId"];
var clientSecret = configuration["clientSecret"];
var certificate = configuration["clientCertificate"];
var certificateStoreName = configuration["clientCertificateStoreName"];
@@ -114,9 +115,14 @@ internal static TokenCredential CreateCredential(IConfiguration configuration)
if (string.Equals(credentialType, "managedidentity", StringComparison.OrdinalIgnoreCase))
{
- if (!string.IsNullOrWhiteSpace(clientId) && !string.IsNullOrWhiteSpace(resourceId))
+ int idCount = 0;
+ idCount += string.IsNullOrWhiteSpace(clientId) ? 0 : 1;
+ idCount += string.IsNullOrWhiteSpace(resourceId) ? 0 : 1;
+ idCount += string.IsNullOrWhiteSpace(objectId) ? 0 : 1;
+
+ if (idCount > 1)
{
- throw new ArgumentException("Cannot specify both 'clientId' and 'managedIdentityResourceId'");
+ throw new ArgumentException("Only one of either 'clientId', 'managedIdentityResourceId', or 'managedIdentityObjectId' can be specified for managed identity.");
}
if (!string.IsNullOrWhiteSpace(resourceId))
@@ -124,6 +130,11 @@ internal static TokenCredential CreateCredential(IConfiguration configuration)
return new ManagedIdentityCredential(new ResourceIdentifier(resourceId));
}
+ if (!string.IsNullOrWhiteSpace(objectId))
+ {
+ return new ManagedIdentityCredential(ManagedIdentityId.FromUserAssignedObjectId(objectId));
+ }
+
return new ManagedIdentityCredential(clientId);
}
@@ -215,6 +226,11 @@ internal static TokenCredential CreateCredential(IConfiguration configuration)
// TODO: More logging
+ if (!string.IsNullOrWhiteSpace(objectId))
+ {
+ throw new ArgumentException("Managed identity 'objectId' is only supported when the credential type is 'managedidentity'.");
+ }
+
if (additionallyAllowedTenantsList != null
|| !string.IsNullOrWhiteSpace(tenantId)
|| !string.IsNullOrWhiteSpace(clientId)
diff --git a/sdk/extensions/Microsoft.Extensions.Azure/tests/ClientFactoryTests.cs b/sdk/extensions/Microsoft.Extensions.Azure/tests/ClientFactoryTests.cs
index 5a62e7cdfac2..1f38862eb0d3 100644
--- a/sdk/extensions/Microsoft.Extensions.Azure/tests/ClientFactoryTests.cs
+++ b/sdk/extensions/Microsoft.Extensions.Azure/tests/ClientFactoryTests.cs
@@ -278,6 +278,7 @@ public void CreatesDefaultAzureCredential(
[Values(true, false)] bool additionalTenants,
[Values(true, false)] bool clientId,
[Values(true, false)] bool tenantId,
+ [Values(true, false)] bool objectId,
[Values(true, false)] bool resourceId)
{
List> configEntries = new();
@@ -299,10 +300,16 @@ public void CreatesDefaultAzureCredential(
{
configEntries.Add(new KeyValuePair("managedIdentityResourceId", resourceIdValue));
}
+ if (objectId)
+ {
+ configEntries.Add(new KeyValuePair("managedIdentityObjectId", "objectId"));
+ }
+
IConfiguration configuration = new ConfigurationBuilder().AddInMemoryCollection(configEntries).Build();
// if both clientId and resourceId set, we expect an ArgumentException
- if (clientId && resourceId)
+ // We also expect an exception if objectId is set for DefaultAzureCredential, as it is only supported for ManagedIdentityCredential
+ if ((clientId && resourceId) || objectId)
{
Assert.Throws(() => ClientFactory.CreateCredential(configuration));
return;
@@ -336,20 +343,27 @@ public void CreatesDefaultAzureCredential(
Assert.AreEqual("tenantId", pwshCredential.TenantId);
}
- // TODO: Since these can't build with project reference, we have to comment them out for now.
- // When we resolve https://github.com/Azure/azure-sdk-for-net/issues/45806, we can add them back.
- //if (clientId)
- //{
- // Assert.AreEqual("clientId", miCredential.Client.ClientId);
- //}
- //if (resourceId)
- //{
- // Assert.AreEqual(resourceIdValue, miCredential.Client.ResourceIdentifier.ToString());
- //}
+ string managedIdentityId;
+ int idType;
+ ReflectIdAndType(miCredential, out managedIdentityId, out idType);
+ if (clientId)
+ {
+ Assert.AreEqual("clientId", managedIdentityId);
+ Assert.AreEqual(1, idType); // 1 is the value for ClientId
+ }
+ if (resourceId)
+ {
+ Assert.AreEqual(resourceIdValue.ToString(), managedIdentityId);
+ Assert.AreEqual(2, idType); // 2 is the value for ResourceId
+ }
+ if (objectId)
+ {
+ Assert.AreEqual("objectId", managedIdentityId);
+ Assert.AreEqual(3, idType); // 3 is the value for ObjectId
+ }
}
[Test]
- [Ignore("This test is failing, ignore it to pass CI. Tracking this in https://github.com/Azure/azure-sdk-for-net/issues/45806")]
public void CreatesManagedServiceIdentityCredentialsWithClientId()
{
IConfiguration configuration = GetConfiguration(
@@ -362,14 +376,15 @@ public void CreatesManagedServiceIdentityCredentialsWithClientId()
Assert.IsInstanceOf(credential);
var managedIdentityCredential = (ManagedIdentityCredential)credential;
- var client = (ManagedIdentityClient)typeof(ManagedIdentityCredential).GetProperty("Client", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityCredential);
- var clientId = typeof(ManagedIdentityClient).GetProperty("ClientId", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(client);
+ string clientId;
+ int idType;
+ ReflectIdAndType(managedIdentityCredential, out clientId, out idType);
Assert.AreEqual("ConfigurationClientId", clientId);
+ Assert.AreEqual(1, idType); // 1 is the value for ClientId
}
[Test]
- [Ignore("This test is failing, ignore it to pass CI. Tracking this in https://github.com/Azure/azure-sdk-for-net/issues/45806")]
public void CreatesManagedServiceIdentityCredentials()
{
IConfiguration configuration = GetConfiguration(
@@ -381,10 +396,12 @@ public void CreatesManagedServiceIdentityCredentials()
Assert.IsInstanceOf(credential);
var managedIdentityCredential = (ManagedIdentityCredential)credential;
- var client = (ManagedIdentityClient)typeof(ManagedIdentityCredential).GetProperty("Client", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityCredential);
- var clientId = typeof(ManagedIdentityClient).GetProperty("ClientId", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(client);
+ string clientId;
+ int idType;
+ ReflectIdAndType(managedIdentityCredential, out clientId, out idType);
Assert.Null(clientId);
+ Assert.AreEqual(0, idType); // 0 is the value for SystemAssigned
}
[Test]
@@ -400,9 +417,33 @@ public void CreatesManagedServiceIdentityCredentialsWithResourceId()
Assert.IsInstanceOf(credential);
var managedIdentityCredential = (ManagedIdentityCredential)credential;
- var resourceId = (string)typeof(ManagedIdentityCredential).GetField("_clientId", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityCredential);
+ string resourceId;
+ int idType;
+ ReflectIdAndType(managedIdentityCredential, out resourceId, out idType);
Assert.AreEqual("ConfigurationResourceId", resourceId);
+ Assert.AreEqual(2, idType); // 2 is the value for ResourceId
+ }
+
+ [Test]
+ public void CreatesManagedServiceIdentityCredentialsWithObjectId()
+ {
+ IConfiguration configuration = GetConfiguration(
+ new KeyValuePair("managedIdentityObjectId", "ConfigurationObjectId"),
+ new KeyValuePair("credential", "managedidentity")
+ );
+
+ var credential = ClientFactory.CreateCredential(configuration);
+
+ Assert.IsInstanceOf(credential);
+ var managedIdentityCredential = (ManagedIdentityCredential)credential;
+
+ string objectId;
+ int idType;
+ ReflectIdAndType(managedIdentityCredential, out objectId, out idType);
+
+ Assert.AreEqual("ConfigurationObjectId", objectId);
+ Assert.AreEqual(3, idType); // 3 is the value for ObjectId
}
[Test]
@@ -419,6 +460,34 @@ public void CreatesManagedServiceIdentityCredentialsThrowsWhenResourceIdAndClien
Throws.InstanceOf().With.Message.Contains("managedIdentityResourceId"));
}
+ [Test]
+ public void CreatesManagedServiceIdentityCredentialsThrowsWhenClientIdAndObjectIdSpecified()
+ {
+ IConfiguration configuration = GetConfiguration(
+ new KeyValuePair("managedIdentityObjectId", "ConfigurationObjectId"),
+ new KeyValuePair("clientId", "ConfigurationClientId"),
+ new KeyValuePair("credential", "managedidentity")
+ );
+
+ Assert.That(
+ () => ClientFactory.CreateCredential(configuration),
+ Throws.InstanceOf().With.Message.Contains("managedIdentityResourceId"));
+ }
+
+ [Test]
+ public void CreatesManagedServiceIdentityCredentialsThrowsWhenResourceIdAndObjectIdSpecified()
+ {
+ IConfiguration configuration = GetConfiguration(
+ new KeyValuePair("managedIdentityObjectId", "ConfigurationObjectId"),
+ new KeyValuePair("managedIdentityResourceId", "ConfigurationResourceId"),
+ new KeyValuePair("credential", "managedidentity")
+ );
+
+ Assert.That(
+ () => ClientFactory.CreateCredential(configuration),
+ Throws.InstanceOf().With.Message.Contains("managedIdentityResourceId"));
+ }
+
[Test]
public void CreatesWorkloadIdentityCredentialsWithOptions()
{
@@ -561,5 +630,14 @@ private IConfiguration GetConfiguration(params KeyValuePair[] it
{
return new ConfigurationBuilder().AddInMemoryCollection(items).Build();
}
+
+ private static void ReflectIdAndType(ManagedIdentityCredential managedIdentityCredential, out string clientId, out int idType)
+ {
+ var managedIdentityClient = typeof(ManagedIdentityCredential).GetProperty("Client", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityCredential);
+ var managedIdentityClientOptions = managedIdentityClient.GetType().GetField("_options", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityClient);
+ var managedIdentityId = managedIdentityClientOptions.GetType().GetProperty("ManagedIdentityId").GetValue(managedIdentityClientOptions);
+ clientId = (string)typeof(ManagedIdentityId).GetField("_userAssignedId", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityId);
+ idType = (int)typeof(ManagedIdentityId).GetField("_idType", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(managedIdentityId);
+ }
}
}