diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs index 0bd82024d376..55b2964a3c0a 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.net8.0.cs @@ -87,6 +87,7 @@ public abstract partial class CloudMachineFeature protected CloudMachineFeature() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public Azure.Provisioning.Primitives.ProvisionableResource Emitted { get { throw null; } protected set { } } + protected internal System.Collections.Generic.Dictionary RequiredSystemRoles { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual void AddTo(Azure.CloudMachine.CloudMachineInfrastructure cm) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs index 0bd82024d376..55b2964a3c0a 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/api/Azure.Provisioning.CloudMachine.netstandard2.0.cs @@ -87,6 +87,7 @@ public abstract partial class CloudMachineFeature protected CloudMachineFeature() { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public Azure.Provisioning.Primitives.ProvisionableResource Emitted { get { throw null; } protected set { } } + protected internal System.Collections.Generic.Dictionary RequiredSystemRoles { get { throw null; } } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] public virtual void AddTo(Azure.CloudMachine.CloudMachineInfrastructure cm) { } [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs index 58495b40f8e4..a7ac7a24c780 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/KeyVaultFeature.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Collections.Generic; using Azure.Provisioning.Authorization; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.Expressions; @@ -44,20 +45,8 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure inf ] }, }; - infrastructure.AddResource(keyVaultResource); - - RoleAssignment ra = keyVaultResource.CreateRoleAssignment(KeyVaultBuiltInRole.KeyVaultAdministrator, RoleManagementPrincipalType.User, infrastructure.PrincipalIdParameter); - infrastructure.AddResource(ra); - - // necessary until ResourceName is settable via AssignRole. - RoleAssignment kvMiRoleAssignment = new RoleAssignment(keyVaultResource.BicepIdentifier + "_" + infrastructure.Identity.BicepIdentifier + "_" + KeyVaultBuiltInRole.GetBuiltInRoleName(KeyVaultBuiltInRole.KeyVaultAdministrator)); - kvMiRoleAssignment.Name = BicepFunction.CreateGuid(keyVaultResource.Id, infrastructure.Identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", KeyVaultBuiltInRole.KeyVaultAdministrator.ToString())); - kvMiRoleAssignment.Scope = new IdentifierExpression(keyVaultResource.BicepIdentifier); - kvMiRoleAssignment.PrincipalType = RoleManagementPrincipalType.ServicePrincipal; - kvMiRoleAssignment.RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", KeyVaultBuiltInRole.KeyVaultAdministrator.ToString()); - kvMiRoleAssignment.PrincipalId = infrastructure.Identity.PrincipalId; - infrastructure.AddResource(kvMiRoleAssignment); + RequiredSystemRoles.Add(keyVaultResource, [(KeyVaultBuiltInRole.GetBuiltInRoleName(KeyVaultBuiltInRole.KeyVaultAdministrator), KeyVaultBuiltInRole.KeyVaultAdministrator.ToString())]); return keyVaultResource; } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs index 2b925548b956..8b0fb82c3d0a 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIFeature.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; using System.Collections.Generic; -using Azure.Provisioning.Authorization; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.CognitiveServices; using Azure.Provisioning.Primitives; @@ -14,25 +13,14 @@ internal class OpenAIFeature : CloudMachineFeature private List _models = new List(); public OpenAIFeature() - {} + { } protected override ProvisionableResource EmitCore(CloudMachineInfrastructure cloudMachine) { CognitiveServicesAccount cognitiveServices = CreateOpenAIAccount(cloudMachine); cloudMachine.AddResource(cognitiveServices); - cloudMachine.AddResource(cognitiveServices.CreateRoleAssignment( - CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor, - RoleManagementPrincipalType.User, - cloudMachine.PrincipalIdParameter) - ); - - cloudMachine.AddResource(cloudMachine.CreateRoleAssignment( - cognitiveServices, - cognitiveServices.Id, - CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor, - cloudMachine.Identity) - ); + RequiredSystemRoles.Add(cognitiveServices, [(CognitiveServicesBuiltInRole.GetBuiltInRoleName(CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor) ,CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor.ToString())]); Emitted = cognitiveServices; @@ -52,7 +40,7 @@ protected override ProvisionableResource EmitCore(CloudMachineInfrastructure clo internal void AddModel(OpenAIModel model) { - if (model.Account!= null) + if (model.Account != null) { throw new InvalidOperationException("Model already added to an account"); } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs index a84f1c151bf4..077927ecf33e 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/AzureSdkExtensions/OpenAIModel.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Diagnostics; using Azure.Provisioning.CloudMachine; using Azure.Provisioning.CognitiveServices; diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineCoreFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineCoreFeature.cs new file mode 100644 index 000000000000..315bf6ac1987 --- /dev/null +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineCoreFeature.cs @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using Azure.Provisioning; +using Azure.Provisioning.Authorization; +using Azure.Provisioning.CloudMachine; +using Azure.Provisioning.EventGrid; +using Azure.Provisioning.Expressions; +using Azure.Provisioning.Primitives; +using Azure.Provisioning.Resources; +using Azure.Provisioning.ServiceBus; +using Azure.Provisioning.Storage; + +namespace Azure.CloudMachine; + +internal class CloudMachineCoreFeature : CloudMachineFeature +{ + public CloudMachineCoreFeature() + { } + protected override ProvisionableResource EmitCore(CloudMachineInfrastructure infrastructure) + { + ManagedServiceIdentity managedServiceIdentity = new() + { + ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, + UserAssignedIdentities = { { BicepFunction.Interpolate($"{infrastructure.Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } + }; + + var _storage = + new StorageAccount("cm_storage", StorageAccount.ResourceVersions.V2023_01_01) + { + Kind = StorageKind.StorageV2, + Sku = new StorageSku { Name = StorageSkuName.StandardLrs }, + IsHnsEnabled = true, + AllowBlobPublicAccess = false + }; + _storage.Identity = managedServiceIdentity; + _storage.Name = infrastructure.Id; + + var _blobs = new BlobService("cm_storage_blobs") + { + Parent = _storage, + }; + var _container = new BlobContainer("cm_storage_blobs_container", "2023-01-01") + { + Parent = _blobs, + Name = "default" + }; + + var _serviceBusNamespace = new ServiceBusNamespace("cm_servicebus") + { + Sku = new ServiceBusSku + { + Name = ServiceBusSkuName.Standard, + Tier = ServiceBusSkuTier.Standard + }, + Name = infrastructure.Id, + }; + var _serviceBusNamespaceAuthorizationRule = new ServiceBusNamespaceAuthorizationRule("cm_servicebus_auth_rule", "2021-11-01") + { + Parent = _serviceBusNamespace, + Rights = [ServiceBusAccessRight.Listen, ServiceBusAccessRight.Send, ServiceBusAccessRight.Manage] + }; + var _serviceBusTopic_private = new ServiceBusTopic("cm_servicebus_topic_private", "2021-11-01") + { + Name = "cm_servicebus_topic_private", + Parent = _serviceBusNamespace, + MaxMessageSizeInKilobytes = 256, + DefaultMessageTimeToLive = TimeSpan.FromDays(14), + RequiresDuplicateDetection = false, + EnableBatchedOperations = true, + SupportOrdering = true, + Status = ServiceBusMessagingEntityStatus.Active + }; + var _serviceBusSubscription_private = new ServiceBusSubscription(CloudMachineInfrastructure.SB_PRIVATE_SUB, "2021-11-01") + { + Name = CloudMachineInfrastructure.SB_PRIVATE_SUB, + Parent = _serviceBusTopic_private, + IsClientAffine = false, + LockDuration = TimeSpan.FromSeconds(30), + RequiresSession = false, + DefaultMessageTimeToLive = TimeSpan.FromDays(14), + DeadLetteringOnFilterEvaluationExceptions = true, + DeadLetteringOnMessageExpiration = true, + MaxDeliveryCount = 10, + EnableBatchedOperations = true, + Status = ServiceBusMessagingEntityStatus.Active + }; + var _serviceBusTopic_default = new ServiceBusTopic("cm_servicebus_topic_default", "2021-11-01") + { + Name = "cm_servicebus_default_topic", + Parent = _serviceBusNamespace, + MaxMessageSizeInKilobytes = 256, + DefaultMessageTimeToLive = TimeSpan.FromDays(14), + RequiresDuplicateDetection = false, + EnableBatchedOperations = true, + SupportOrdering = true, + Status = ServiceBusMessagingEntityStatus.Active + }; + var _serviceBusSubscription_default = new ServiceBusSubscription("cm_servicebus_subscription_default", "2021-11-01") + { + Name = "cm_servicebus_subscription_default", + Parent = _serviceBusTopic_default, + IsClientAffine = false, + LockDuration = TimeSpan.FromSeconds(30), + RequiresSession = false, + DefaultMessageTimeToLive = TimeSpan.FromDays(14), + DeadLetteringOnFilterEvaluationExceptions = true, + DeadLetteringOnMessageExpiration = true, + MaxDeliveryCount = 10, + EnableBatchedOperations = true, + Status = ServiceBusMessagingEntityStatus.Active + }; + var _eventGridTopic_blobs = new SystemTopic("cm_eventgrid_topic_blob", "2022-06-15") + { + TopicType = "Microsoft.Storage.StorageAccounts", + Source = _storage.Id, + Identity = new() + { + ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, + UserAssignedIdentities = { { BicepFunction.Interpolate($"{infrastructure.Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } + }, + Name = infrastructure.Id + }; + var _eventGridSubscription_blobs = new SystemTopicEventSubscription("cm_eventgrid_subscription_blob", "2022-06-15") + { + Name = "cm-eventgrid-subscription-blob", + Parent = _eventGridTopic_blobs, + DeliveryWithResourceIdentity = new DeliveryWithResourceIdentity + { + Identity = new EventSubscriptionIdentity + { + IdentityType = EventSubscriptionIdentityType.UserAssigned, + UserAssignedIdentity = infrastructure.Identity.Id + }, + Destination = new ServiceBusTopicEventSubscriptionDestination + { + ResourceId = _serviceBusTopic_private.Id + } + }, + Filter = new EventSubscriptionFilter + { + IncludedEventTypes = + [ + "Microsoft.Storage.BlobCreated", + "Microsoft.Storage.BlobDeleted", + "Microsoft.Storage.BlobRenamed" + ], + IsAdvancedFilteringOnArraysEnabled = true + }, + EventDeliverySchema = EventDeliverySchema.EventGridSchema, + RetryPolicy = new EventSubscriptionRetryPolicy + { + MaxDeliveryAttempts = 30, + EventTimeToLiveInMinutes = 1440 + } + }; + + infrastructure.AddResource(infrastructure.PrincipalIdParameter); + infrastructure.AddResource(infrastructure.Identity); + infrastructure.AddResource(_storage); + RequiredSystemRoles.Add( + _storage, + [ + (StorageBuiltInRole.GetBuiltInRoleName(StorageBuiltInRole.StorageBlobDataContributor),StorageBuiltInRole.StorageBlobDataContributor.ToString()), + (StorageBuiltInRole.GetBuiltInRoleName(StorageBuiltInRole.StorageTableDataContributor), StorageBuiltInRole.StorageTableDataContributor.ToString()) + ]); + + infrastructure.AddResource(_container); + infrastructure.AddResource(_blobs); + infrastructure.AddResource(_serviceBusNamespace); + + RequiredSystemRoles.Add( + _serviceBusNamespace, + [ + (ServiceBusBuiltInRole.GetBuiltInRoleName(ServiceBusBuiltInRole.AzureServiceBusDataOwner), ServiceBusBuiltInRole.AzureServiceBusDataOwner.ToString()), + ]); + + var role = ServiceBusBuiltInRole.AzureServiceBusDataSender; + RoleAssignment roleAssignment = new RoleAssignment("cm_servicebus_role"); + roleAssignment.Name = BicepFunction.CreateGuid(_serviceBusNamespace.Id, infrastructure.Identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.ToString())); + roleAssignment.Scope = new IdentifierExpression(_serviceBusNamespace.BicepIdentifier); + roleAssignment.PrincipalType = RoleManagementPrincipalType.ServicePrincipal; + roleAssignment.RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.ToString()); + roleAssignment.PrincipalId = infrastructure.Identity.PrincipalId; + infrastructure.AddResource(roleAssignment); + + // the role assignment must exist before the system topic event subscription is created. + _eventGridSubscription_blobs.DependsOn.Add(roleAssignment); + + infrastructure.AddResource(_serviceBusNamespaceAuthorizationRule); + infrastructure.AddResource(_serviceBusTopic_private); + infrastructure.AddResource(_serviceBusTopic_default); + infrastructure.AddResource(_serviceBusSubscription_private); + infrastructure.AddResource(_serviceBusSubscription_default); + infrastructure.AddResource(_eventGridSubscription_blobs); + infrastructure.AddResource(_eventGridTopic_blobs); + + // Placeholders for now. + infrastructure.AddResource(new ProvisioningOutput($"storage_name", typeof(string)) { Value = _storage.Name }); + infrastructure.AddResource(new ProvisioningOutput($"servicebus_name", typeof(string)) { Value = _serviceBusNamespace.Name }); + + return _storage; + } +} diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs index 0c3f1ece48d1..da68e676e6c7 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineFeature.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Collections.Generic; using System.ComponentModel; using Azure.CloudMachine; using Azure.Provisioning.Primitives; @@ -15,7 +16,8 @@ public abstract class CloudMachineFeature [EditorBrowsable(EditorBrowsableState.Never)] public void Emit(CloudMachineInfrastructure cm) { - if (Emitted != null) return; + if (Emitted != null) + return; ProvisionableResource provisionable = EmitCore(cm); Emitted = provisionable; } @@ -24,4 +26,6 @@ public void Emit(CloudMachineInfrastructure cm) [EditorBrowsable(EditorBrowsableState.Never)] public ProvisionableResource Emitted { get; protected set; } = default!; + + protected internal Dictionary RequiredSystemRoles { get; } = []; } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs index e883d9defd02..9a2847f25469 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/CloudMachineInfrastructure.cs @@ -3,12 +3,8 @@ using System; using Azure.Provisioning.Authorization; -using Azure.Provisioning.EventGrid; using Azure.Provisioning.Expressions; -using Azure.Provisioning.Resources; using Azure.Provisioning.Roles; -using Azure.Provisioning.ServiceBus; -using Azure.Provisioning.Storage; using Azure.Provisioning.Primitives; using System.Collections.Generic; using Azure.Provisioning; @@ -29,25 +25,6 @@ public class CloudMachineInfrastructure public FeatureCollection Features { get; } = new(); internal List Endpoints { get; } = new(); - // storage - private StorageAccount _storage; - private BlobService _blobs; - private BlobContainer _container; - - // servicebus - private ServiceBusNamespace _serviceBusNamespace; - private ServiceBusNamespaceAuthorizationRule _serviceBusNamespaceAuthorizationRule; - - private ServiceBusTopic _serviceBusTopic_default; - private ServiceBusSubscription _serviceBusSubscription_default; - - private ServiceBusTopic _serviceBusTopic_private; - private ServiceBusSubscription _serviceBusSubscription_private; - - // eventgrid - private SystemTopic _eventGridTopic_blobs; - private SystemTopicEventSubscription _eventGridSubscription_blobs; - public UserAssignedIdentity Identity { get; private set; } public string Id => _cmid; @@ -74,141 +51,7 @@ public CloudMachineInfrastructure(string cmId) Identity = new UserAssignedIdentity("cm_identity"); Identity.Name = _cmid; _infrastructure.Add(new ProvisioningOutput($"cm_managed_identity_id", typeof(string)) { Value = Identity.Id }); - ManagedServiceIdentity managedServiceIdentity = new() - { - ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, - UserAssignedIdentities = { { BicepFunction.Interpolate($"{Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } - }; - - _storage = - new StorageAccount("cm_storage", StorageAccount.ResourceVersions.V2023_01_01) - { - Kind = StorageKind.StorageV2, - Sku = new StorageSku { Name = StorageSkuName.StandardLrs }, - IsHnsEnabled = true, - AllowBlobPublicAccess = false - }; - _storage.Identity = managedServiceIdentity; - _storage.Name = _cmid; - - _blobs = new("cm_storage_blobs") - { - Parent = _storage, - }; - _container = new BlobContainer("cm_storage_blobs_container", "2023-01-01") - { - Parent = _blobs, - Name = "default" - }; - - _serviceBusNamespace = new("cm_servicebus") - { - Sku = new ServiceBusSku - { - Name = ServiceBusSkuName.Standard, - Tier = ServiceBusSkuTier.Standard - }, - Name = _cmid, - }; - _serviceBusNamespaceAuthorizationRule = new("cm_servicebus_auth_rule", "2021-11-01") - { - Parent = _serviceBusNamespace, - Rights = [ServiceBusAccessRight.Listen, ServiceBusAccessRight.Send, ServiceBusAccessRight.Manage] - }; - _serviceBusTopic_private = new("cm_servicebus_topic_private", "2021-11-01") - { - Name = "cm_servicebus_topic_private", - Parent = _serviceBusNamespace, - MaxMessageSizeInKilobytes = 256, - DefaultMessageTimeToLive = TimeSpan.FromDays(14), - RequiresDuplicateDetection = false, - EnableBatchedOperations = true, - SupportOrdering = true, - Status = ServiceBusMessagingEntityStatus.Active - }; - _serviceBusSubscription_private = new(SB_PRIVATE_SUB, "2021-11-01") - { - Name = SB_PRIVATE_SUB, - Parent = _serviceBusTopic_private, - IsClientAffine = false, - LockDuration = TimeSpan.FromSeconds(30), - RequiresSession = false, - DefaultMessageTimeToLive = TimeSpan.FromDays(14), - DeadLetteringOnFilterEvaluationExceptions = true, - DeadLetteringOnMessageExpiration = true, - MaxDeliveryCount = 10, - EnableBatchedOperations = true, - Status = ServiceBusMessagingEntityStatus.Active - }; - _serviceBusTopic_default = new("cm_servicebus_topic_default", "2021-11-01") - { - Name = "cm_servicebus_default_topic", - Parent = _serviceBusNamespace, - MaxMessageSizeInKilobytes = 256, - DefaultMessageTimeToLive = TimeSpan.FromDays(14), - RequiresDuplicateDetection = false, - EnableBatchedOperations = true, - SupportOrdering = true, - Status = ServiceBusMessagingEntityStatus.Active - }; - _serviceBusSubscription_default = new("cm_servicebus_subscription_default", "2021-11-01") - { - Name = "cm_servicebus_subscription_default", - Parent = _serviceBusTopic_default, - IsClientAffine = false, - LockDuration = TimeSpan.FromSeconds(30), - RequiresSession = false, - DefaultMessageTimeToLive = TimeSpan.FromDays(14), - DeadLetteringOnFilterEvaluationExceptions = true, - DeadLetteringOnMessageExpiration = true, - MaxDeliveryCount = 10, - EnableBatchedOperations = true, - Status = ServiceBusMessagingEntityStatus.Active - }; - _eventGridTopic_blobs = new("cm_eventgrid_topic_blob", "2022-06-15") - { - TopicType = "Microsoft.Storage.StorageAccounts", - Source = _storage.Id, - Identity = new() - { - ManagedServiceIdentityType = ManagedServiceIdentityType.UserAssigned, - UserAssignedIdentities = { { BicepFunction.Interpolate($"{Identity.Id}").Compile().ToString(), new UserAssignedIdentityDetails() } } - }, - Name = _cmid - }; - _eventGridSubscription_blobs = new("cm_eventgrid_subscription_blob", "2022-06-15") - { - Name = "cm-eventgrid-subscription-blob", - Parent = _eventGridTopic_blobs, - DeliveryWithResourceIdentity = new DeliveryWithResourceIdentity - { - Identity = new EventSubscriptionIdentity - { - IdentityType = EventSubscriptionIdentityType.UserAssigned, - UserAssignedIdentity = Identity.Id - }, - Destination = new ServiceBusTopicEventSubscriptionDestination - { - ResourceId = _serviceBusTopic_private.Id - } - }, - Filter = new EventSubscriptionFilter - { - IncludedEventTypes = - [ - "Microsoft.Storage.BlobCreated", - "Microsoft.Storage.BlobDeleted", - "Microsoft.Storage.BlobRenamed" - ], - IsAdvancedFilteringOnArraysEnabled = true - }, - EventDeliverySchema = EventDeliverySchema.EventGridSchema, - RetryPolicy = new EventSubscriptionRetryPolicy - { - MaxDeliveryAttempts = 30, - EventTimeToLiveInMinutes = 1440 - } - }; + Features.Add(new CloudMachineCoreFeature()); } public void AddResource(NamedProvisionableConstruct resource) @@ -230,8 +73,14 @@ public void AddEndpoints() public ProvisioningPlan Build(ProvisioningBuildOptions? context = null) { + if (context == null) + context = new ProvisioningBuildOptions(); + Features.Emit(this); + // This must occur after the features have been emitted. + context.InfrastructureResolvers.Add(new RoleResolver(Features.RoleAnnotations, [Identity], [PrincipalIdParameter])); + // Always add a default location parameter. // azd assumes there will be a location parameter for every module. // The Infrastructure location resolver will resolve unset Location properties to this parameter. @@ -241,45 +90,6 @@ public ProvisioningPlan Build(ProvisioningBuildOptions? context = null) Value = BicepFunction.GetResourceGroup().Location }); - _infrastructure.Add(PrincipalIdParameter); - //Add(PrincipalTypeParameter); - //Add(PrincipalNameParameter); - - var storageBlobDataContributor = StorageBuiltInRole.StorageBlobDataContributor; - var storageTableDataContributor = StorageBuiltInRole.StorageTableDataContributor; - var azureServiceBusDataSender = ServiceBusBuiltInRole.AzureServiceBusDataSender; - var azureServiceBusDataOwner = ServiceBusBuiltInRole.AzureServiceBusDataOwner; - - _infrastructure.Add(Identity); - _infrastructure.Add(_storage); - _infrastructure.Add(_storage.CreateRoleAssignment(storageBlobDataContributor, RoleManagementPrincipalType.User, PrincipalIdParameter)); - _infrastructure.Add(CreateRoleAssignment(_storage, _storage.Id, storageBlobDataContributor, Identity)); - _infrastructure.Add(_storage.CreateRoleAssignment(storageTableDataContributor, RoleManagementPrincipalType.User, PrincipalIdParameter)); - _infrastructure.Add(CreateRoleAssignment(_storage, _storage.Id, storageTableDataContributor, Identity)); - _infrastructure.Add(_container); - _infrastructure.Add(_blobs); - _infrastructure.Add(_serviceBusNamespace); - _infrastructure.Add(_serviceBusNamespace.CreateRoleAssignment(azureServiceBusDataOwner, RoleManagementPrincipalType.User, PrincipalIdParameter)); - _infrastructure.Add(CreateRoleAssignment(_serviceBusNamespace,_serviceBusNamespace.Id, azureServiceBusDataOwner, Identity)); - _infrastructure.Add(_serviceBusNamespaceAuthorizationRule); - _infrastructure.Add(_serviceBusTopic_private); - _infrastructure.Add(_serviceBusTopic_default); - _infrastructure.Add(_serviceBusSubscription_private); - _infrastructure.Add(_serviceBusSubscription_default); - - RoleAssignment roleAssignment = CreateRoleAssignment(_serviceBusNamespace, _serviceBusNamespace.Id, azureServiceBusDataSender, Identity); - _infrastructure.Add(roleAssignment); - - CreateRoleAssignment(_serviceBusNamespace, _serviceBusNamespace.Id, azureServiceBusDataSender, Identity); - // the role assignment must exist before the system topic event subscription is created. - _eventGridSubscription_blobs.DependsOn.Add(roleAssignment); - _infrastructure.Add(_eventGridSubscription_blobs); - _infrastructure.Add(_eventGridTopic_blobs); - - // Placeholders for now. - _infrastructure.Add(new ProvisioningOutput($"storage_name", typeof(string)) { Value = _storage.Name }); - _infrastructure.Add(new ProvisioningOutput($"servicebus_name", typeof(string)) { Value = _serviceBusNamespace.Name }); - // Add any add-on resources to the infrastructure. foreach (Provisionable resource in _resources) { @@ -289,20 +99,43 @@ public ProvisioningPlan Build(ProvisioningBuildOptions? context = null) return _infrastructure.Build(context); } - // Temporary until the bug is fixed in the CDK generator which uses the PrincipalId instead of the Id in BicepFunction.CreateGuid. - internal RoleAssignment CreateRoleAssignment(ProvisionableResource resource, BicepValue Id, object role, UserAssignedIdentity identity) + internal class RoleResolver(Dictionary annotations, IEnumerable managedIdentities, IEnumerable> userPrincipals) : InfrastructureResolver { - if (role is null) throw new ArgumentException("Role must not be null.", nameof(role)); - var method = role.GetType().GetMethod("GetBuiltInRoleName", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); - string roleName = (string)method!.Invoke(null, [role])!; - - return new($"{resource.BicepIdentifier}_{identity.BicepIdentifier}_{roleName}") + public override IEnumerable ResolveResources(IEnumerable resources, ProvisioningBuildOptions options) { - Name = BicepFunction.CreateGuid(Id, identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role!.ToString()!)), - Scope = new IdentifierExpression(resource.BicepIdentifier), - PrincipalType = RoleManagementPrincipalType.ServicePrincipal, - RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.ToString()!), - PrincipalId = identity.PrincipalId - }; + foreach (Provisionable provisionable in base.ResolveResources(resources, options)) + { + yield return provisionable; + if (annotations.TryGetValue(provisionable, out (string RoleName, string RoleId)[]? roles) && provisionable is ProvisionableResource resource && roles is not null) + { + foreach ((string RoleName, string RoleId) role in roles) + { + foreach (BicepValue userPrincipal in userPrincipals) + { + yield return new RoleAssignment($"{resource.BicepIdentifier}_{userPrincipal.Value.ToString().Replace('-', '_')}_{role.RoleName}") + { + Name = BicepFunction.CreateGuid(resource.BicepIdentifier, userPrincipal, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId)), + Scope = new IdentifierExpression(resource.BicepIdentifier), + PrincipalType = RoleManagementPrincipalType.User, + RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId), + PrincipalId = userPrincipal + }; + } + + foreach (UserAssignedIdentity identity in managedIdentities) + { + yield return new RoleAssignment($"{resource.BicepIdentifier}_{identity.BicepIdentifier}_{role.RoleName}") + { + Name = BicepFunction.CreateGuid(resource.BicepIdentifier, identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId)), + Scope = new IdentifierExpression(resource.BicepIdentifier), + PrincipalType = RoleManagementPrincipalType.ServicePrincipal, + RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", role.RoleId), + PrincipalId = identity.PrincipalId + }; + } + } + } + } + } } } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs index 119315122127..50cdbfadbbff 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/src/CDKLevel3/FeatureCollection.cs @@ -3,7 +3,9 @@ using System; using System.Collections.Generic; +using System.Linq; using Azure.Provisioning.CloudMachine; +using Azure.Provisioning.Primitives; namespace Azure.CloudMachine; @@ -12,7 +14,13 @@ public class FeatureCollection private CloudMachineFeature[] _items = new CloudMachineFeature[4]; private int _count; - public IEnumerable FindAll() where T: CloudMachineFeature + internal Dictionary RoleAnnotations => + _items.Take(_count) + .Select(f => f.RequiredSystemRoles) + .SelectMany(d => d) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + public IEnumerable FindAll() where T : CloudMachineFeature { for (int i = 0; i < _count; i++) { @@ -44,7 +52,8 @@ internal void Emit(CloudMachineInfrastructure infrastructure) int index = 0; while (true) { - if (index >= _count) break; + if (index >= _count) + break; CloudMachineFeature feature = _items[index++]; feature.Emit(infrastructure); } diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj index 1564e40b8072..0a891c2b442f 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Azure.Provisioning.CloudMachine.Tests.csproj @@ -7,4 +7,9 @@ + + + + + diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs index cd87f20d7fe9..7c6a99f3254f 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/CloudMachineTests.cs @@ -3,6 +3,9 @@ #nullable enable +using System; +using System.IO; +using System.Linq; using Azure.CloudMachine.AppService; using Azure.CloudMachine.KeyVault; using Azure.CloudMachine.OpenAI; @@ -15,13 +18,15 @@ public class CloudMachineTests [Test] public void GenerateBicep() { - CloudMachineCommands.Execute(["-bicep"], (CloudMachineInfrastructure infrastructure) => - { - infrastructure.AddFeature(new KeyVaultFeature()); - infrastructure.AddFeature(new OpenAIModel("gpt-35-turbo", "0125")); - infrastructure.AddFeature(new OpenAIModel("text-embedding-ada-002", "2", AIModelKind.Embedding)); - infrastructure.AddFeature(new AppServiceFeature()); - }, exitProcessIfHandled:false); + CloudMachineInfrastructure infra = new("cm0c420d2f21084cd"); + infra.AddFeature(new KeyVaultFeature()); + infra.AddFeature(new OpenAIModel("gpt-35-turbo", "0125")); + infra.AddFeature(new OpenAIModel("text-embedding-ada-002", "2", AIModelKind.Embedding)); + infra.AddFeature(new AppServiceFeature()); + + string actualBicep = infra!.Build().Compile().FirstOrDefault().Value; + string expectedBicep = File.ReadAllText(Path.Combine(TestContext.CurrentContext.TestDirectory, "Data", "GenerateBicep.bicep")).Replace("\r\n", Environment.NewLine); + Assert.AreEqual(expectedBicep, actualBicep); } [Ignore("no recordings yet")] diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep new file mode 100644 index 000000000000..9cbe83a6469c --- /dev/null +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/Data/GenerateBicep.bicep @@ -0,0 +1,401 @@ +@description('The location for the resource(s) to be deployed.') +param location string = resourceGroup().location + +param principalId string + +resource cm_identity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: 'cm0c420d2f21084cd' + location: location +} + +resource cm_storage 'Microsoft.Storage/storageAccounts@2023-01-01' = { + name: 'cm0c420d2f21084cd' + kind: 'StorageV2' + location: location + sku: { + name: 'Standard_LRS' + } + properties: { + allowBlobPublicAccess: false + isHnsEnabled: true + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } +} + +resource cm_storage_00000000_0000_0000_0000_000000000000_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: 'User' + } + scope: cm_storage +} + +resource cm_storage_cm_identity_StorageBlobDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe') + principalType: 'ServicePrincipal' + } + scope: cm_storage +} + +resource cm_storage_00000000_0000_0000_0000_000000000000_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalType: 'User' + } + scope: cm_storage +} + +resource cm_storage_cm_identity_StorageTableDataContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_storage', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '0a9a7e1f-b9d0-4cc4-a60d-0319b160aaa3') + principalType: 'ServicePrincipal' + } + scope: cm_storage +} + +resource cm_storage_blobs_container 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { + name: 'default' + parent: cm_storage_blobs +} + +resource cm_storage_blobs 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = { + name: 'default' + parent: cm_storage +} + +resource cm_servicebus 'Microsoft.ServiceBus/namespaces@2024-01-01' = { + name: 'cm0c420d2f21084cd' + location: location + sku: { + name: 'Standard' + tier: 'Standard' + } +} + +resource cm_servicebus_00000000_0000_0000_0000_000000000000_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_servicebus', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + principalType: 'User' + } + scope: cm_servicebus +} + +resource cm_servicebus_cm_identity_AzureServiceBusDataOwner 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_servicebus', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '090c5cfd-751d-490a-894a-3ce6f1109419') + principalType: 'ServicePrincipal' + } + scope: cm_servicebus +} + +resource cm_servicebus_role 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(cm_servicebus.id, cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '69a216fc-b8fb-44d8-bc22-1f3c2cd27a39') + principalType: 'ServicePrincipal' + } + scope: cm_servicebus +} + +resource cm_servicebus_auth_rule 'Microsoft.ServiceBus/namespaces/AuthorizationRules@2021-11-01' = { + name: take('cm_servicebus_auth_rule-${uniqueString(resourceGroup().id)}', 50) + properties: { + rights: [ + 'Listen' + 'Send' + 'Manage' + ] + } + parent: cm_servicebus +} + +resource cm_servicebus_topic_private 'Microsoft.ServiceBus/namespaces/topics@2021-11-01' = { + name: 'cm_servicebus_topic_private' + properties: { + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + maxMessageSizeInKilobytes: 256 + requiresDuplicateDetection: false + status: 'Active' + supportOrdering: true + } + parent: cm_servicebus +} + +resource cm_servicebus_topic_default 'Microsoft.ServiceBus/namespaces/topics@2021-11-01' = { + name: 'cm_servicebus_default_topic' + properties: { + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + maxMessageSizeInKilobytes: 256 + requiresDuplicateDetection: false + status: 'Active' + supportOrdering: true + } + parent: cm_servicebus +} + +resource cm_servicebus_subscription_private 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = { + name: 'cm_servicebus_subscription_private' + properties: { + deadLetteringOnFilterEvaluationExceptions: true + deadLetteringOnMessageExpiration: true + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + isClientAffine: false + lockDuration: 'PT30S' + maxDeliveryCount: 10 + requiresSession: false + status: 'Active' + } + parent: cm_servicebus_topic_private +} + +resource cm_servicebus_subscription_default 'Microsoft.ServiceBus/namespaces/topics/subscriptions@2021-11-01' = { + name: 'cm_servicebus_subscription_default' + properties: { + deadLetteringOnFilterEvaluationExceptions: true + deadLetteringOnMessageExpiration: true + defaultMessageTimeToLive: 'P14D' + enableBatchedOperations: true + isClientAffine: false + lockDuration: 'PT30S' + maxDeliveryCount: 10 + requiresSession: false + status: 'Active' + } + parent: cm_servicebus_topic_default +} + +resource cm_eventgrid_subscription_blob 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2022-06-15' = { + name: 'cm-eventgrid-subscription-blob' + properties: { + deliveryWithResourceIdentity: { + identity: { + type: 'UserAssigned' + userAssignedIdentity: cm_identity.id + } + destination: { + endpointType: 'ServiceBusTopic' + properties: { + resourceId: cm_servicebus_topic_private.id + } + } + } + eventDeliverySchema: 'EventGridSchema' + filter: { + includedEventTypes: [ + 'Microsoft.Storage.BlobCreated' + 'Microsoft.Storage.BlobDeleted' + 'Microsoft.Storage.BlobRenamed' + ] + enableAdvancedFilteringOnArrays: true + } + retryPolicy: { + maxDeliveryAttempts: 30 + eventTimeToLiveInMinutes: 1440 + } + } + parent: cm_eventgrid_topic_blob + dependsOn: [ + cm_servicebus_role + ] +} + +resource cm_eventgrid_topic_blob 'Microsoft.EventGrid/systemTopics@2022-06-15' = { + name: 'cm0c420d2f21084cd' + location: location + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } + properties: { + source: cm_storage.id + topicType: 'Microsoft.Storage.StorageAccounts' + } +} + +resource cm_kv 'Microsoft.KeyVault/vaults@2023-07-01' = { + name: 'cm0c420d2f21084cd' + location: location + properties: { + tenantId: subscription().tenantId + sku: { + family: 'A' + name: 'standard' + } + accessPolicies: [ + { + tenantId: cm_identity.properties.tenantId + objectId: principalId + permissions: { + secrets: [ + 'get' + 'set' + ] + } + } + ] + enabledForDeployment: true + } +} + +resource cm_kv_00000000_0000_0000_0000_000000000000_KeyVaultAdministrator 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_kv', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + principalType: 'User' + } + scope: cm_kv +} + +resource cm_kv_cm_identity_KeyVaultAdministrator 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('cm_kv', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '00482a5a-887f-4fb3-b363-3b7fe8e74483') + principalType: 'ServicePrincipal' + } + scope: cm_kv +} + +resource openai 'Microsoft.CognitiveServices/accounts@2024-10-01' = { + name: 'cm0c420d2f21084cd' + location: location + kind: 'OpenAI' + properties: { + customSubDomainName: 'cm0c420d2f21084cd' + publicNetworkAccess: 'Enabled' + } + sku: { + name: 'S0' + } +} + +resource openai_00000000_0000_0000_0000_000000000000_CognitiveServicesOpenAIContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('openai', principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')) + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + principalType: 'User' + } + scope: openai +} + +resource openai_cm_identity_CognitiveServicesOpenAIContributor 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid('openai', cm_identity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')) + properties: { + principalId: cm_identity.properties.principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + principalType: 'ServicePrincipal' + } + scope: openai +} + +resource openai_cm0c420d2f21084cd_chat 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview' = { + name: 'cm0c420d2f21084cd_chat' + properties: { + model: { + format: 'OpenAI' + name: 'gpt-35-turbo' + version: '0125' + } + raiPolicyName: 'Microsoft.DefaultV2' + versionUpgradeOption: 'OnceNewDefaultVersionAvailable' + } + sku: { + name: 'Standard' + capacity: 120 + } + parent: openai +} + +resource openai_cm0c420d2f21084cd_embedding 'Microsoft.CognitiveServices/accounts/deployments@2024-06-01-preview' = { + name: 'cm0c420d2f21084cd_embedding' + properties: { + model: { + format: 'OpenAI' + name: 'text-embedding-ada-002' + version: '2' + } + raiPolicyName: 'Microsoft.DefaultV2' + versionUpgradeOption: 'OnceNewDefaultVersionAvailable' + } + sku: { + name: 'Standard' + capacity: 120 + } + parent: openai + dependsOn: [ + openai_cm0c420d2f21084cd_chat + ] +} + +resource cm_hosting_plan 'Microsoft.Web/serverfarms@2024-04-01' = { + name: 'cm0c420d2f21084cd' + location: location + kind: 'app' + sku: { + name: 'F1' + tier: 'Free' + } +} + +resource cm_website 'Microsoft.Web/sites@2024-04-01' = { + name: 'cm0c420d2f21084cd' + location: location + properties: { + serverFarmId: cm_hosting_plan.id + enabled: true + httpsOnly: true + siteConfig: { + appSettings: [ + { + name: 'CLOUDMACHINE_MANAGED_IDENTITY_CLIENT_ID' + value: cm_identity.properties.clientId + } + ] + webSocketsEnabled: true + http20Enabled: true + minTlsVersion: '1.2' + } + } + identity: { + type: 'UserAssigned' + userAssignedIdentities: { + '${cm_identity.id}': { } + } + } + kind: 'app' + tags: { + 'azd-service-name': 'cm0c420d2f21084cd' + } +} + +output cm_managed_identity_id string = cm_identity.id + +output storage_name string = cm_storage.name + +output servicebus_name string = cm_servicebus.name \ No newline at end of file diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs index 7951f968ace0..bc0d0c495955 100644 --- a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/TdkTests.cs @@ -53,7 +53,7 @@ @get @route("send") Send(@query message: string) : { } - """; + """.Replace("\r\n", Environment.NewLine); } internal interface IAssistantService diff --git a/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/appsettings.json b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/appsettings.json new file mode 100644 index 000000000000..983d023f5e2b --- /dev/null +++ b/sdk/provisioning/Azure.Provisioning.CloudMachine/tests/appsettings.json @@ -0,0 +1 @@ +{"CloudMachine":{"ID":"cm0c420d2f21084cd"}} \ No newline at end of file