From 11df6494ab58f5d537262f0bf37f1dee18a6c9a0 Mon Sep 17 00:00:00 2001 From: Jordan Matthiesen <1333029+jmatthiesen@users.noreply.github.com> Date: Thu, 17 Oct 2024 23:49:11 -0700 Subject: [PATCH] Adding initial infra created from Aspire AppHost. --- azure.yaml | 8 +++ infra/main.bicep | 52 ++++++++++++++++++ infra/main.parameters.json | 16 ++++++ infra/openai/openai.module.bicep | 52 ++++++++++++++++++ infra/resources.bicep | 93 ++++++++++++++++++++++++++++++++ 5 files changed, 221 insertions(+) create mode 100644 azure.yaml create mode 100644 infra/main.bicep create mode 100644 infra/main.parameters.json create mode 100644 infra/openai/openai.module.bicep create mode 100644 infra/resources.bicep diff --git a/azure.yaml b/azure.yaml new file mode 100644 index 0000000..44c16cb --- /dev/null +++ b/azure.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json + +name: ai-chat-aspire-sk-csharp +services: + app: + language: dotnet + project: ./src/AIChatApp.AppHost/AIChatApp.AppHost.csproj + host: containerapp diff --git a/infra/main.bicep b/infra/main.bicep new file mode 100644 index 0000000..31d38b2 --- /dev/null +++ b/infra/main.bicep @@ -0,0 +1,52 @@ +targetScope = 'subscription' + +@minLength(1) +@maxLength(64) +@description('Name of the environment that can be used as part of naming resource convention, the name of the resource group for your application will use this name, prefixed with rg-') +param environmentName string + +@minLength(1) +@description('The location used for all deployed resources') +param location string + +@description('Id of the user or app to assign application roles') +param principalId string = '' + +var tags = { + 'azd-env-name': environmentName +} + +resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = { + name: 'rg-${environmentName}' + location: location + tags: tags +} + +module resources 'resources.bicep' = { + scope: rg + name: 'resources' + params: { + location: location + tags: tags + principalId: principalId + } +} + +module openai 'openai/openai.module.bicep' = { + name: 'openai' + scope: rg + params: { + location: location + principalId: resources.outputs.MANAGED_IDENTITY_PRINCIPAL_ID + principalType: 'ServicePrincipal' + } +} +output MANAGED_IDENTITY_CLIENT_ID string = resources.outputs.MANAGED_IDENTITY_CLIENT_ID +output MANAGED_IDENTITY_NAME string = resources.outputs.MANAGED_IDENTITY_NAME +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = resources.outputs.AZURE_LOG_ANALYTICS_WORKSPACE_NAME +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = resources.outputs.AZURE_CONTAINER_REGISTRY_ENDPOINT +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = resources.outputs.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID +output AZURE_CONTAINER_APPS_ENVIRONMENT_NAME string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_NAME +output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_ID +output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = resources.outputs.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN +output OPENAI_CONNECTIONSTRING string = openai.outputs.connectionString diff --git a/infra/main.parameters.json b/infra/main.parameters.json new file mode 100644 index 0000000..db7cedc --- /dev/null +++ b/infra/main.parameters.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "principalId": { + "value": "${AZURE_PRINCIPAL_ID}" + }, + "environmentName": { + "value": "${AZURE_ENV_NAME}" + }, + "location": { + "value": "${AZURE_LOCATION}" + } + } + } + \ No newline at end of file diff --git a/infra/openai/openai.module.bicep b/infra/openai/openai.module.bicep new file mode 100644 index 0000000..6cde0a3 --- /dev/null +++ b/infra/openai/openai.module.bicep @@ -0,0 +1,52 @@ +targetScope = 'resourceGroup' + +@description('') +param location string = resourceGroup().location + +@description('') +param principalId string + +@description('') +param principalType string + +resource cognitiveServicesAccount_wXAGTFUId 'Microsoft.CognitiveServices/accounts@2023-05-01' = { + name: toLower(take('openai${uniqueString(resourceGroup().id)}', 24)) + location: location + kind: 'OpenAI' + sku: { + name: 'S0' + } + properties: { + customSubDomainName: toLower(take(concat('openai', uniqueString(resourceGroup().id)), 24)) + publicNetworkAccess: 'Enabled' + disableLocalAuth: true + } +} + +resource roleAssignment_Hsk8rxWY8 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + scope: cognitiveServicesAccount_wXAGTFUId + name: guid(cognitiveServicesAccount_wXAGTFUId.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442')) + properties: { + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'a001fd3d-188f-4b5d-821b-7da978bf7442') + principalId: principalId + principalType: principalType + } +} + +resource cognitiveServicesAccountDeployment_6E9woetGC 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = { + parent: cognitiveServicesAccount_wXAGTFUId + name: 'chat' + sku: { + name: 'GlobalStandard' + capacity: 10 + } + properties: { + model: { + format: 'OpenAI' + name: 'gpt-4o' + version: '2024-05-13' + } + } +} + +output connectionString string = 'Endpoint=${cognitiveServicesAccount_wXAGTFUId.properties.endpoint}' diff --git a/infra/resources.bicep b/infra/resources.bicep new file mode 100644 index 0000000..c3a95bd --- /dev/null +++ b/infra/resources.bicep @@ -0,0 +1,93 @@ +@description('The location used for all deployed resources') +param location string = resourceGroup().location +@description('Id of the user or app to assign application roles') +param principalId string = '' + + +@description('Tags that will be applied to all resources') +param tags object = {} + +var resourceToken = uniqueString(resourceGroup().id) + +resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: 'mi-${resourceToken}' + location: location + tags: tags +} + +resource containerRegistry 'Microsoft.ContainerRegistry/registries@2023-07-01' = { + name: replace('acr-${resourceToken}', '-', '') + location: location + sku: { + name: 'Basic' + } + tags: tags +} + +resource caeMiRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerRegistry.id, managedIdentity.id, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d')) + scope: containerRegistry + properties: { + principalId: managedIdentity.properties.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '7f951dda-4ed3-4680-a7ca-43fe172d538d') + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = { + name: 'law-${resourceToken}' + location: location + properties: { + sku: { + name: 'PerGB2018' + } + } + tags: tags +} + +resource containerAppEnvironment 'Microsoft.App/managedEnvironments@2024-02-02-preview' = { + name: 'cae-${resourceToken}' + location: location + properties: { + workloadProfiles: [{ + workloadProfileType: 'Consumption' + name: 'consumption' + }] + appLogsConfiguration: { + destination: 'log-analytics' + logAnalyticsConfiguration: { + customerId: logAnalyticsWorkspace.properties.customerId + sharedKey: logAnalyticsWorkspace.listKeys().primarySharedKey + } + } + } + tags: tags + + resource aspireDashboard 'dotNetComponents' = { + name: 'aspire-dashboard' + properties: { + componentType: 'AspireDashboard' + } + } + +} + +resource explicitContributorUserRoleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(containerAppEnvironment.id, principalId, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')) + scope: containerAppEnvironment + properties: { + principalId: principalId + roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + } +} + +output MANAGED_IDENTITY_CLIENT_ID string = managedIdentity.properties.clientId +output MANAGED_IDENTITY_NAME string = managedIdentity.name +output MANAGED_IDENTITY_PRINCIPAL_ID string = managedIdentity.properties.principalId +output AZURE_LOG_ANALYTICS_WORKSPACE_NAME string = logAnalyticsWorkspace.name +output AZURE_LOG_ANALYTICS_WORKSPACE_ID string = logAnalyticsWorkspace.id +output AZURE_CONTAINER_REGISTRY_ENDPOINT string = containerRegistry.properties.loginServer +output AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID string = managedIdentity.id +output AZURE_CONTAINER_APPS_ENVIRONMENT_NAME string = containerAppEnvironment.name +output AZURE_CONTAINER_APPS_ENVIRONMENT_ID string = containerAppEnvironment.id +output AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN string = containerAppEnvironment.properties.defaultDomain