Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.ClientModel.Primitives;
using System.Collections.Generic;
using Azure.ResourceManager.Models;

namespace Azure.ResourceManager
{
public partial class AzureResourceManagerContext
{
partial void AddAdditionalFactories(Dictionary<Type, Func<ModelReaderWriterTypeBuilder>> factories)
{
factories.Add(typeof(ManagedServiceIdentity), () => new ManagedServiceIdentityTypeBuilder());
}

private class ManagedServiceIdentityTypeBuilder : ModelReaderWriterTypeBuilder
{
protected override Type BuilderType => typeof(ManagedServiceIdentity);

protected override object CreateInstance()
{
return new ManagedServiceIdentity(ManagedServiceIdentityType.None);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,55 @@ namespace Azure.ResourceManager.Models
[JsonConverter(typeof(ManagedServiceIdentityConverter))]
public partial class ManagedServiceIdentity : IJsonModel<ManagedServiceIdentity>
{
internal void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options, JsonSerializerOptions jOptions = default)
private const string SystemAssignedUserAssignedV3Value = "SystemAssigned,UserAssigned";

// This method checks if the format string in options.Format ends with the "|v3" suffix.
// The "|v3" suffix indicates that the ManagedServiceIdentityType format is version 3.
// If the suffix is present, it is removed, and the base format is returned via the 'format' parameter.
// This allows the method to handle version-specific logic while preserving the base format.
private static bool UseManagedServiceIdentityV3(ModelReaderWriterOptions options, out string format)
{
var originalFormat = options.Format.AsSpan();
if (originalFormat.Length > 3)
{
var v3Format = "|v3".AsSpan();
if (originalFormat.EndsWith(v3Format))
{
format = originalFormat.Slice(0, originalFormat.Length - v3Format.Length).ToString();
return true;
}
}

format = options.Format;
return false;
}

void IJsonModel<ManagedServiceIdentity>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
var useManagedServiceIdentityV3 = UseManagedServiceIdentityV3(options, out string optionsFormat);
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;
if (format != "J")
{
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
}

writer.WriteStartObject();
JsonSerializer.Serialize(writer, ManagedServiceIdentityType, jOptions);
if (options.Format != "W" && Optional.IsDefined(PrincipalId))
writer.WritePropertyName("type"u8);
if (useManagedServiceIdentityV3 && ManagedServiceIdentityType == ManagedServiceIdentityType.SystemAssignedUserAssigned)
{
writer.WriteStringValue(SystemAssignedUserAssignedV3Value);
}
else
{
writer.WriteStringValue(ManagedServiceIdentityType.ToString());
}

if (optionsFormat != "W" && Optional.IsDefined(PrincipalId))
{
writer.WritePropertyName("principalId"u8);
writer.WriteStringValue(PrincipalId.Value);
}
if (options.Format != "W" && Optional.IsDefined(TenantId))
if (optionsFormat != "W" && Optional.IsDefined(TenantId))
{
writer.WritePropertyName("tenantId"u8);
writer.WriteStringValue(TenantId.Value);
Expand All @@ -45,21 +78,24 @@ internal void Write(Utf8JsonWriter writer, ModelReaderWriterOptions options, Jso
foreach (var item in UserAssignedIdentities)
{
writer.WritePropertyName(item.Key);
JsonSerializer.Serialize(writer, item.Value);
if (item.Value is null)
{
writer.WriteNullValue();
}
else
{
((IJsonModel<UserAssignedIdentity>)item.Value).Write(writer, new ModelReaderWriterOptions(optionsFormat));
}
}
writer.WriteEndObject();
}
writer.WriteEndObject();
}

void IJsonModel<ManagedServiceIdentity>.Write(Utf8JsonWriter writer, ModelReaderWriterOptions options)
{
Write(writer, options, null);
}

ManagedServiceIdentity IJsonModel<ManagedServiceIdentity>.Create(ref Utf8JsonReader reader, ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
UseManagedServiceIdentityV3(options, out string optionsFormat);
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;
if (format != "J")
{
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
Expand All @@ -71,7 +107,8 @@ ManagedServiceIdentity IJsonModel<ManagedServiceIdentity>.Create(ref Utf8JsonRea

BinaryData IPersistableModel<ManagedServiceIdentity>.Write(ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
UseManagedServiceIdentityV3(options, out string optionsFormat);
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;

switch (format)
{
Expand All @@ -80,7 +117,7 @@ BinaryData IPersistableModel<ManagedServiceIdentity>.Write(ModelReaderWriterOpti
case "bicep":
return SerializeBicep(options);
default:
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{options.Format}' format.");
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
}
}

Expand Down Expand Up @@ -168,14 +205,17 @@ private void AppendChildObject(StringBuilder stringBuilder, object childObject,
}
}

internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonElement element, ModelReaderWriterOptions options, JsonSerializerOptions jOptions)
internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonElement element, ModelReaderWriterOptions options = null)
{
options ??= new ModelReaderWriterOptions("W");
var useManagedServiceIdentityV3 = UseManagedServiceIdentityV3(options, out string format);
options = new ModelReaderWriterOptions(format);

if (element.ValueKind == JsonValueKind.Null)
{
return null;
}

Guid? principalId = default;
Guid? tenantId = default;
ManagedServiceIdentityType type = default;
Expand All @@ -202,7 +242,15 @@ internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonEle
}
if (property.NameEquals("type"u8))
{
type = JsonSerializer.Deserialize<ManagedServiceIdentityType>($"{{{property}}}", jOptions);
var propertyValue = property.Value.GetString();
if (useManagedServiceIdentityV3 && propertyValue == SystemAssignedUserAssignedV3Value)
{
type = ManagedServiceIdentityType.SystemAssignedUserAssigned;
}
else
{
type = new ManagedServiceIdentityType(propertyValue);
}
continue;
}
if (property.NameEquals("userAssignedIdentities"u8))
Expand All @@ -214,7 +262,7 @@ internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonEle
Dictionary<ResourceIdentifier, UserAssignedIdentity> dictionary = new Dictionary<ResourceIdentifier, UserAssignedIdentity>();
foreach (var property0 in property.Value.EnumerateObject())
{
dictionary.Add(new ResourceIdentifier(property0.Name), JsonSerializer.Deserialize<UserAssignedIdentity>(property0.Value.GetRawText()));
dictionary.Add(new ResourceIdentifier(property0.Name), ModelReaderWriter.Read<UserAssignedIdentity>(new BinaryData(Encoding.UTF8.GetBytes(property0.Value.GetRawText())), options, AzureResourceManagerContext.Default));
}
userAssignedIdentities = dictionary;
continue;
Expand All @@ -223,14 +271,11 @@ internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonEle
return new ManagedServiceIdentity(principalId, tenantId, type, userAssignedIdentities ?? new ChangeTrackingDictionary<ResourceIdentifier, UserAssignedIdentity>());
}

internal static ManagedServiceIdentity DeserializeManagedServiceIdentity(JsonElement element, ModelReaderWriterOptions options = null)
{
return DeserializeManagedServiceIdentity(element, options, null);
}

ManagedServiceIdentity IPersistableModel<ManagedServiceIdentity>.Create(BinaryData data, ModelReaderWriterOptions options)
{
var format = options.Format == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : options.Format;
options ??= new ModelReaderWriterOptions("W");
var useManagedServiceIdentityV3 = UseManagedServiceIdentityV3(options, out string optionsFormat);
var format = optionsFormat == "W" ? ((IPersistableModel<ManagedServiceIdentity>)this).GetFormatFromOptions(options) : optionsFormat;

switch (format)
{
Expand All @@ -240,22 +285,30 @@ ManagedServiceIdentity IPersistableModel<ManagedServiceIdentity>.Create(BinaryDa
return DeserializeManagedServiceIdentity(document.RootElement, options);
}
default:
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{options.Format}' format.");
throw new FormatException($"The model {nameof(ManagedServiceIdentity)} does not support '{format}' format.");
}
}

string IPersistableModel<ManagedServiceIdentity>.GetFormatFromOptions(ModelReaderWriterOptions options) => "J";

internal partial class ManagedServiceIdentityConverter : JsonConverter<ManagedServiceIdentity>
{
private static readonly ModelReaderWriterOptions V3Options = new ModelReaderWriterOptions("W|v3");

// This method checks if the ManagedServiceIdentityTypeV3Converter exists and it indicates that the ManagedServiceIdentityType format is version 3.
// Then, the format string in options.Format should be "W|v3", otherwise the default options.Format is "W".
// TODO: Remove this method when ManagedServiceIdentityTypeV3Converter is removed from the codebase after we apply the latest genertor changes.
private bool UseManagedServiceIdentityV3(JsonSerializerOptions options)
=> options is not null && options.Converters.Any(x => x.ToString().EndsWith("ManagedServiceIdentityTypeV3Converter"));

public override void Write(Utf8JsonWriter writer, ManagedServiceIdentity model, JsonSerializerOptions options)
{
model.Write(writer, new ModelReaderWriterOptions("W"), options);
((IJsonModel<ManagedServiceIdentity>)model).Write(writer, UseManagedServiceIdentityV3(options) ? V3Options : new ModelReaderWriterOptions("W"));
}
public override ManagedServiceIdentity Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
using var document = JsonDocument.ParseValue(ref reader);
return DeserializeManagedServiceIdentity(document.RootElement, null, options);
return DeserializeManagedServiceIdentity(document.RootElement, UseManagedServiceIdentityV3(options) ? V3Options : null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Azure.Core;
using Azure.Core.TestFramework;
using Azure.ResourceManager.ManagementGroups;
using Azure.ResourceManager.ManagementGroups.Models;
using Azure.ResourceManager.Models;
using Azure.ResourceManager.Resources;
using Azure.ResourceManager.Resources.Models;
using NUnit.Framework;
Expand Down Expand Up @@ -112,45 +109,12 @@ public async Task Get()
Assert.ThrowsAsync<ArgumentNullException>(async () => _ = await rg.GetPolicyAssignments().GetAsync(null));
}

#pragma warning disable CS0618 // This type is obsolete and will be removed in a future release.
[TestCase]
[RecordedTest]
public async Task TestManagedIdentity()
{
SubscriptionResource subscription = await Client.GetDefaultSubscriptionAsync();
string rgName = Recording.GenerateAssetName("testRg-");
ResourceGroupResource rg = await CreateResourceGroup(subscription, rgName);
string policyAssignmentName = Recording.GenerateAssetName("polAssign-");
PolicyAssignmentData input = new PolicyAssignmentData
{
DisplayName = $"Test ${policyAssignmentName}",
PolicyDefinitionId = "/providers/Microsoft.Authorization/policyDefinitions/06a78e20-9358-41c9-923c-fb736d382a4d",
Identity = new SystemAssignedServiceIdentity(SystemAssignedServiceIdentityType.SystemAssigned),
Location = AzureLocation.WestUS
};
Assert.AreEqual(SystemAssignedServiceIdentityType.SystemAssigned, input.Identity.SystemAssignedServiceIdentityType);
Assert.AreEqual(ManagedServiceIdentityType.SystemAssigned, input.ManagedIdentity.ManagedServiceIdentityType);
ArmOperation<PolicyAssignmentResource> lro = await rg.GetPolicyAssignments().CreateOrUpdateAsync(WaitUntil.Completed, policyAssignmentName, input);
PolicyAssignmentResource policyAssignment = lro.Value;
Assert.AreEqual(policyAssignmentName, policyAssignment.Data.Name);
Assert.AreEqual(policyAssignment.Data.Identity.PrincipalId, policyAssignment.Data.ManagedIdentity.PrincipalId);
Assert.AreEqual(policyAssignment.Data.Identity.TenantId, policyAssignment.Data.ManagedIdentity.TenantId);
Assert.AreEqual(ManagedServiceIdentityType.SystemAssigned, policyAssignment.Data.ManagedIdentity.ManagedServiceIdentityType);
Assert.AreEqual(SystemAssignedServiceIdentityType.SystemAssigned, policyAssignment.Data.Identity.SystemAssignedServiceIdentityType);
policyAssignment.Data.ManagedIdentity.ManagedServiceIdentityType = ManagedServiceIdentityType.None;
lro = await policyAssignment.UpdateAsync(WaitUntil.Completed, policyAssignment.Data);
PolicyAssignmentResource updatedPolicyAssignment = lro.Value;
Assert.AreEqual(ManagedServiceIdentityType.None, updatedPolicyAssignment.Data.ManagedIdentity.ManagedServiceIdentityType);
Assert.AreEqual(SystemAssignedServiceIdentityType.None, updatedPolicyAssignment.Data.Identity.SystemAssignedServiceIdentityType);
}

private void AssertValidPolicyAssignment(PolicyAssignmentResource model, PolicyAssignmentResource getResult)
{
Assert.AreEqual(model.Data.Name, getResult.Data.Name);
Assert.AreEqual(model.Data.Id, getResult.Data.Id);
Assert.AreEqual(model.Data.ResourceType, getResult.Data.ResourceType);
Assert.AreEqual(model.Data.Location, getResult.Data.Location);
Assert.AreEqual(model.Data.Identity, getResult.Data.Identity);
Assert.AreEqual(model.Data.DisplayName, getResult.Data.DisplayName);
Assert.AreEqual(model.Data.PolicyDefinitionId, getResult.Data.PolicyDefinitionId);
Assert.AreEqual(model.Data.Scope, getResult.Data.Scope);
Expand Down Expand Up @@ -181,6 +145,5 @@ private void AssertValidPolicyAssignment(PolicyAssignmentResource model, PolicyA
}
}
}
#pragma warning restore CS0618 // This type is obsolete and will be removed in a future release.
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.ClientModel.Primitives;
using System.IO;
using System.Text;
using System.Text.Json;
using Azure.Core;
using NUnit.Framework;

#nullable enable

namespace Azure.ResourceManager.Tests
{
internal static class JsonAsserts
Expand All @@ -22,13 +25,14 @@ public static void AssertSerialization(string expected, IUtf8JsonSerializable se
Assert.AreEqual(expected, text);
}

public static void AssertConverterSerialization(string expected, object model, JsonSerializerOptions options = default)
public static void AssertConverterSerialization<T>(string expected, T model, ModelReaderWriterOptions? options = null)
{
using var memoryStream = new MemoryStream();

using (var writer = new Utf8JsonWriter(memoryStream))
{
JsonSerializer.Serialize(writer, model, options);
var jsonModel = model as IJsonModel<T>;
jsonModel?.Write(writer, options ?? new ModelReaderWriterOptions("W"));
}

var text = Encoding.UTF8.GetString(memoryStream.ToArray());
Expand Down
Loading