Skip to content

Commit fabfa6c

Browse files
Added the ability to provision AOAI as an optional component (Azure#46570)
* fixed cminfra * openai provisioning works * open ai client added * progress * changed ai to key auth * moved CM to WorkspaceClient abstraction * refactored built-in methods and extension methods * openai works * updated api file * disabled live tests * updated version * small tweaks * updated api file * PR feedback * removed stj override
1 parent 8a252ab commit fabfa6c

22 files changed

+679
-290
lines changed

sdk/provisioning/Azure.Provisioning.CloudMachine/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## 1.0.0-beta.1 (Unreleased)
44

5+
## 1.0.0-beta.2 (Unreleased)
6+
57
### Features Added
68

79
### Breaking Changes
Lines changed: 64 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,83 @@
11
namespace Azure.CloudMachine
22
{
3-
public partial class ClientCache
3+
public partial class CloudMachineClient : Azure.CloudMachine.CloudMachineWorkspace
44
{
5-
public ClientCache() { }
6-
public T Get<T>(string id, System.Func<T> value) where T : class { throw null; }
5+
public CloudMachineClient(Azure.Core.TokenCredential? credential = null, Microsoft.Extensions.Configuration.IConfiguration? configuration = null) : base (default(Azure.Core.TokenCredential), default(Microsoft.Extensions.Configuration.IConfiguration)) { }
6+
public Azure.CloudMachine.MessagingServices Messaging { get { throw null; } }
7+
public Azure.CloudMachine.StorageServices Storage { get { throw null; } }
78
}
8-
public partial class CloudMachineClient
9+
public partial class CloudMachineWorkspace : Azure.Core.WorkspaceClient
910
{
10-
protected CloudMachineClient() { }
11-
public CloudMachineClient(Azure.Identity.DefaultAzureCredential? credential = null, Microsoft.Extensions.Configuration.IConfiguration? configuration = null) { }
11+
public CloudMachineWorkspace(Azure.Core.TokenCredential? credential = null, Microsoft.Extensions.Configuration.IConfiguration? configuration = null) { }
1212
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
13-
public Azure.CloudMachine.ClientCache ClientCache { get { throw null; } }
14-
public Azure.Core.TokenCredential Credential { get { throw null; } }
15-
public string Id { get { throw null; } }
13+
public override Azure.Core.TokenCredential Credential { get { throw null; } }
1614
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
17-
public Azure.CloudMachine.CloudMachineClient.CloudMachineProperties Properties { get { throw null; } }
15+
public string Id { get { throw null; } }
1816
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
1917
public override bool Equals(object? obj) { throw null; }
2018
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
19+
public override Azure.Core.ClientConfiguration? GetConfiguration(string clientId, string? instanceId = null) { throw null; }
20+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
2121
public override int GetHashCode() { throw null; }
2222
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
2323
public override string ToString() { throw null; }
24-
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
25-
public partial struct CloudMachineProperties
26-
{
27-
private object _dummy;
28-
private int _dummyPrimitive;
29-
public System.Uri BlobServiceUri { get { throw null; } }
30-
public System.Uri DefaultContainerUri { get { throw null; } }
31-
public System.Uri KeyVaultUri { get { throw null; } }
32-
public string ServiceBusNamespace { get { throw null; } }
33-
}
3424
}
35-
public static partial class MessagingServices
25+
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
26+
public readonly partial struct MessagingServices
3627
{
37-
public static void Send(this Azure.CloudMachine.CloudMachineClient cm, object serializable) { }
28+
private readonly object _dummy;
29+
private readonly int _dummyPrimitive;
30+
public void SendMessage(object serializable) { }
31+
public void WhenMessageReceived(System.Action<string> received) { }
3832
}
39-
public static partial class StorageServices
33+
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
34+
public readonly partial struct StorageServices
4035
{
41-
public static System.BinaryData Download(this Azure.CloudMachine.CloudMachineClient cm, string name) { throw null; }
42-
public static string Upload(this Azure.CloudMachine.CloudMachineClient cm, object json, string? name = null) { throw null; }
36+
private readonly object _dummy;
37+
private readonly int _dummyPrimitive;
38+
public System.BinaryData DownloadBlob(string name) { throw null; }
39+
public string UploadBlob(object json, string? name = null) { throw null; }
40+
public void WhenBlobCreated(System.Func<string, System.Threading.Tasks.Task> function) { }
41+
public void WhenBlobUploaded(System.Action<string> function) { }
42+
}
43+
}
44+
namespace Azure.Core
45+
{
46+
public partial class ClientCache
47+
{
48+
public ClientCache() { }
49+
public T Get<T>(string id, System.Func<T> value) where T : class { throw null; }
50+
}
51+
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
52+
public readonly partial struct ClientConfiguration
53+
{
54+
private readonly object _dummy;
55+
private readonly int _dummyPrimitive;
56+
public ClientConfiguration(string endpoint, string? apiKey = null) { throw null; }
57+
public string? ApiKey { get { throw null; } }
58+
public Azure.Core.CredentialType CredentialType { get { throw null; } }
59+
public string Endpoint { get { throw null; } }
60+
}
61+
public enum CredentialType
62+
{
63+
EntraId = 0,
64+
ApiKey = 1,
65+
}
66+
public abstract partial class WorkspaceClient
67+
{
68+
protected WorkspaceClient() { }
69+
public abstract Azure.Core.TokenCredential Credential { get; }
70+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
71+
public Azure.Core.ClientCache Subclients { get { throw null; } }
72+
public abstract Azure.Core.ClientConfiguration? GetConfiguration(string clientId, string? instanceId = null);
4373
}
4474
}
4575
namespace Azure.Provisioning.CloudMachine
4676
{
4777
public abstract partial class CloudMachineFeature
4878
{
4979
protected CloudMachineFeature() { }
80+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
5081
public abstract void AddTo(Azure.Provisioning.CloudMachine.CloudMachineInfrastructure cm);
5182
}
5283
public partial class CloudMachineInfrastructure
@@ -65,24 +96,26 @@ namespace Azure.Provisioning.CloudMachine.KeyVault
6596
{
6697
public static partial class KeyVaultExtensions
6798
{
68-
public static Azure.Security.KeyVault.Secrets.SecretClient GetKeyVaultSecretClient(this Azure.CloudMachine.CloudMachineClient client) { throw null; }
99+
public static Azure.Security.KeyVault.Secrets.SecretClient GetKeyVaultSecretsClient(this Azure.Core.WorkspaceClient workspace) { throw null; }
69100
}
70101
public partial class KeyVaultFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature
71102
{
72-
public KeyVaultFeature() { }
103+
public KeyVaultFeature(Azure.Provisioning.KeyVault.KeyVaultSku? sku = null) { }
73104
public Azure.Provisioning.KeyVault.KeyVaultSku Sku { get { throw null; } set { } }
74-
public override void AddTo(Azure.Provisioning.CloudMachine.CloudMachineInfrastructure cm) { }
105+
public override void AddTo(Azure.Provisioning.CloudMachine.CloudMachineInfrastructure infrastructure) { }
75106
}
76107
}
77108
namespace Azure.Provisioning.CloudMachine.OpenAI
78109
{
79110
public partial class OpenAIFeature : Azure.Provisioning.CloudMachine.CloudMachineFeature
80111
{
81-
public OpenAIFeature() { }
82-
public override void AddTo(Azure.Provisioning.CloudMachine.CloudMachineInfrastructure cm) { }
112+
public OpenAIFeature(string model, string modelVersion) { }
113+
public string Model { get { throw null; } }
114+
public string ModelVersion { get { throw null; } }
115+
public override void AddTo(Azure.Provisioning.CloudMachine.CloudMachineInfrastructure cloudMachine) { }
83116
}
84117
public static partial class OpenAIFeatureExtensions
85118
{
86-
public static Azure.Security.KeyVault.Secrets.SecretClient GetOpenAIClient(this Azure.CloudMachine.CloudMachineClient client) { throw null; }
119+
public static OpenAI.Chat.ChatClient GetOpenAIChatClient(this Azure.Core.WorkspaceClient workspace) { throw null; }
87120
}
88121
}

sdk/provisioning/Azure.Provisioning.CloudMachine/src/Azure.Provisioning.CloudMachine.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Description>Azure.Provisioning.CloudMachine simplifies declarative resource provisioning in .NET.</Description>
5-
<Version>1.0.0-beta.1</Version>
5+
<Version>1.0.0-beta.2</Version>
66
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
77
<LangVersion>12</LangVersion>
88

@@ -11,6 +11,7 @@
1111
</PropertyGroup>
1212

1313
<ItemGroup>
14+
<PackageReference Include="Azure.AI.OpenAI" />
1415
<PackageReference Include="Azure.Identity" />
1516
<PackageReference Include="Azure.Messaging.ServiceBus" />
1617
<PackageReference Include="Azure.Security.KeyVault.Secrets" />
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using Azure.Core;
6+
using Azure.Provisioning.Authorization;
7+
using Azure.Provisioning.Expressions;
8+
using Azure.Provisioning.KeyVault;
9+
using Azure.Security.KeyVault.Secrets;
10+
11+
namespace Azure.Provisioning.CloudMachine.KeyVault;
12+
13+
public class KeyVaultFeature : CloudMachineFeature
14+
{
15+
public KeyVaultSku Sku { get; set; }
16+
17+
public KeyVaultFeature(KeyVaultSku? sku = default)
18+
{
19+
if (sku == null)
20+
{
21+
sku = new KeyVaultSku { Name = KeyVaultSkuName.Standard, Family = KeyVaultSkuFamily.A, };
22+
}
23+
Sku = sku;
24+
}
25+
public override void AddTo(CloudMachineInfrastructure infrastructure)
26+
{
27+
// Add a KeyVault to the CloudMachine infrastructure.
28+
KeyVaultService keyVaultResource = new("cm_kv")
29+
{
30+
Name = infrastructure.Id,
31+
Properties =
32+
new KeyVaultProperties
33+
{
34+
Sku = this.Sku,
35+
TenantId = BicepFunction.GetSubscription().TenantId,
36+
EnabledForDeployment = true,
37+
AccessPolicies = [
38+
new KeyVaultAccessPolicy() {
39+
ObjectId = infrastructure.PrincipalIdParameter,
40+
Permissions = new IdentityAccessPermissions() {
41+
Secrets = [IdentityAccessSecretPermission.Get, IdentityAccessSecretPermission.Set]
42+
},
43+
TenantId = infrastructure.Identity.TenantId
44+
}
45+
]
46+
},
47+
};
48+
49+
infrastructure.AddResource(keyVaultResource);
50+
51+
RoleAssignment ra = keyVaultResource.CreateRoleAssignment(KeyVaultBuiltInRole.KeyVaultAdministrator, RoleManagementPrincipalType.User, infrastructure.PrincipalIdParameter);
52+
infrastructure.AddResource(ra);
53+
54+
// necessary until ResourceName is settable via AssignRole.
55+
RoleAssignment kvMiRoleAssignment = new RoleAssignment(keyVaultResource.IdentifierName + "_" + infrastructure.Identity.IdentifierName + "_" + KeyVaultBuiltInRole.GetBuiltInRoleName(KeyVaultBuiltInRole.KeyVaultAdministrator));
56+
kvMiRoleAssignment.Name = BicepFunction.CreateGuid(keyVaultResource.Id, infrastructure.Identity.Id, BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", KeyVaultBuiltInRole.KeyVaultAdministrator.ToString()));
57+
kvMiRoleAssignment.Scope = new IdentifierExpression(keyVaultResource.IdentifierName);
58+
kvMiRoleAssignment.PrincipalType = RoleManagementPrincipalType.ServicePrincipal;
59+
kvMiRoleAssignment.RoleDefinitionId = BicepFunction.GetSubscriptionResourceId("Microsoft.Authorization/roleDefinitions", KeyVaultBuiltInRole.KeyVaultAdministrator.ToString());
60+
kvMiRoleAssignment.PrincipalId = infrastructure.Identity.PrincipalId;
61+
infrastructure.AddResource(kvMiRoleAssignment);
62+
}
63+
}
64+
65+
public static class KeyVaultExtensions
66+
{
67+
public static SecretClient GetKeyVaultSecretsClient(this WorkspaceClient workspace)
68+
{
69+
ClientConfiguration? connectionMaybe = workspace.GetConfiguration(typeof(SecretClient).FullName);
70+
if (connectionMaybe == null)
71+
{
72+
throw new Exception("Connection not found");
73+
}
74+
75+
ClientConfiguration connection = connectionMaybe.Value;
76+
if (connection.CredentialType == CredentialType.EntraId)
77+
{
78+
return new(new Uri(connection.Endpoint), workspace.Credential);
79+
}
80+
throw new Exception("ApiKey not supported");
81+
}
82+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.ClientModel;
6+
using System.ClientModel.Primitives;
7+
using System.Diagnostics.Contracts;
8+
using Azure.AI.OpenAI;
9+
using Azure.CloudMachine;
10+
using Azure.Core;
11+
using Azure.Provisioning.Authorization;
12+
using Azure.Provisioning.CognitiveServices;
13+
using OpenAI.Chat;
14+
15+
namespace Azure.Provisioning.CloudMachine.OpenAI;
16+
17+
public class OpenAIFeature : CloudMachineFeature
18+
{
19+
public string Model { get; }
20+
public string ModelVersion { get; }
21+
22+
public OpenAIFeature(string model, string modelVersion) { Model = model; ModelVersion = modelVersion; }
23+
24+
public override void AddTo(CloudMachineInfrastructure cloudMachine)
25+
{
26+
CognitiveServicesAccount cognitiveServices = new("openai")
27+
{
28+
Name = cloudMachine.Id,
29+
Kind = "OpenAI",
30+
Sku = new CognitiveServicesSku { Name = "S0" },
31+
Properties = new CognitiveServicesAccountProperties()
32+
{
33+
PublicNetworkAccess = ServiceAccountPublicNetworkAccess.Enabled,
34+
CustomSubDomainName = cloudMachine.Id
35+
},
36+
};
37+
38+
cloudMachine.AddResource(cognitiveServices.CreateRoleAssignment(
39+
CognitiveServicesBuiltInRole.CognitiveServicesOpenAIContributor,
40+
RoleManagementPrincipalType.User,
41+
cloudMachine.PrincipalIdParameter)
42+
);
43+
44+
// TODO: if we every support more than one deployment, they need to be chained using DependsOn.
45+
// The reason is that deployments need to be deployed/created serially.
46+
CognitiveServicesAccountDeployment deployment = new("openai_deployment", "2023-05-01")
47+
{
48+
Parent = cognitiveServices,
49+
Name = cloudMachine.Id,
50+
Properties = new CognitiveServicesAccountDeploymentProperties()
51+
{
52+
Model = new CognitiveServicesAccountDeploymentModel() {
53+
Name = this.Model,
54+
Format = "OpenAI",
55+
Version = this.ModelVersion
56+
}
57+
},
58+
};
59+
60+
cloudMachine.AddResource(cognitiveServices);
61+
cloudMachine.AddResource(deployment);
62+
}
63+
}
64+
65+
public static class OpenAIFeatureExtensions
66+
{
67+
public static ChatClient GetOpenAIChatClient(this WorkspaceClient workspace)
68+
{
69+
string chatClientId = typeof(ChatClient).FullName;
70+
71+
ChatClient client = workspace.Subclients.Get(chatClientId, () =>
72+
{
73+
string azureOpenAIClientId = typeof(AzureOpenAIClient).FullName;
74+
75+
AzureOpenAIClient aoia = workspace.Subclients.Get(azureOpenAIClientId, () =>
76+
{
77+
ClientConfiguration? connectionMaybe = workspace.GetConfiguration(typeof(AzureOpenAIClient).FullName);
78+
if (connectionMaybe == null) throw new Exception("Connection not found");
79+
80+
ClientConfiguration connection = connectionMaybe.Value;
81+
Uri endpoint = new(connection.Endpoint);
82+
var clientOptions = new AzureOpenAIClientOptions();
83+
if (connection.CredentialType == CredentialType.EntraId)
84+
{
85+
AzureOpenAIClient aoai = new(endpoint, workspace.Credential, clientOptions);
86+
return aoai;
87+
}
88+
else
89+
{
90+
AzureOpenAIClient aoai = new(endpoint, new ApiKeyCredential(connection.ApiKey!), clientOptions);
91+
return aoai;
92+
}
93+
});
94+
95+
string azureOpenAIChatClientId = typeof(ChatClient).FullName;
96+
ClientConfiguration? connectionMaybe = workspace.GetConfiguration(azureOpenAIChatClientId);
97+
if (connectionMaybe == null) throw new Exception("Connection not found");
98+
var connection = connectionMaybe.Value;
99+
ChatClient chat = aoia.GetChatClient(connection.Endpoint);
100+
return chat;
101+
});
102+
103+
return client;
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4+
using System.ComponentModel;
5+
46
namespace Azure.Provisioning.CloudMachine;
57

68
public abstract class CloudMachineFeature
79
{
10+
[EditorBrowsable(EditorBrowsableState.Never)]
811
public abstract void AddTo(CloudMachineInfrastructure cm);
912
}

0 commit comments

Comments
 (0)