working version of a managed app

This commit is contained in:
Josh Bradley 2025-01-08 11:38:03 -05:00
parent bc24bfad1c
commit ca0c4890ad
5 changed files with 279 additions and 55 deletions

View File

@ -1,17 +1,41 @@
# Managed App Instructions # Managed App Instructions
This guide is a temporary document that walks through the progress made so far to convert the graphrag solution accelerator to a managed app. This guide is a temporary document that walks through the process to convert the graphrag solution accelerator to a managed app.
1. Auto format the bicep code ### 1. Auto format the bicep code
As a precaution, start by auto-formating and linting the bicep code to detect any mistakes early-on.
```bash ```bash
cd <repo_root_directory>/infra
find . -type f -name "*.bicep" -exec az bicep format --file {} \; find . -type f -name "*.bicep" -exec az bicep format --file {} \;
find . -type f -name "*.bicep" -exec az bicep lint --file {} \;
``` ```
2. Convert bicep -> ARM ### 2. Convert bicep -> ARM
```bash ```bash
az bicep build --file main.bicep --outfile managed-app/mainTemplate.json az bicep build --file main.bicep --outfile managed-app/mainTemplate.json
``` ```
3. Test the Portal Interface ### 3. Create & test the Azure portal interface
Use the [Azure Portal Sandbox](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/SandboxBlade) to test and make any UI changes in `managed-app/createUiDefinition.json`. To make additional changes to the Azure portal experience, start by reading some [documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/managed-applications/create-uidefinition-overview) and copying the contents of `createUiDefinition.json` into the sandbox environment.
Use the [Azure Portal Sandbox](https://portal.azure.com/#blade/Microsoft_Azure_CreateUIDef/SandboxBlade) to test and make any UI changes that are defined in [createUiDefinition.json](createUiDefinition.json). To make additional changes to the Azure portal experience, start by reading some [documentation](https://learn.microsoft.com/en-us/azure/azure-resource-manager/managed-applications/create-uidefinition-overview) and copying the contents of `createUiDefinition.json` into the sandbox environment.
### 4. Package up the managed app code
The name of the final two files (`mainTemplate.json` and `createUiDefinition.json`) cannot be changed. The file names are also case-sensitive and cannot be changed at this time. Managed apps require these files to be packaged up into a zip file (where the json files must be at the root directory).
```bash
cd <repo_root_directory>/infra/managed-app
zip -rj managed-app.zip .
```
This zip file can then be uploaded to an Azure Storage location when setting up a [Service Catalog Managed Application Definition](https://ms.portal.azure.com/#view/Microsoft_Azure_Marketplace/GalleryItemDetailsBladeNopdl/id/Microsoft.ApplianceDefinition/selectionMode~/false/resourceGroupId//resourceGroupLocation//dontDiscardJourney~/false/selectedMenuId/home/launchingContext~/%7B%22galleryItemId%22%3A%22Microsoft.ApplianceDefinition%22%2C%22source%22%3A%5B%22GalleryFeaturedMenuItemPart%22%2C%22VirtualizedTileDetails%22%5D%2C%22menuItemId%22%3A%22home%22%2C%22subMenuItemId%22%3A%22Search%20results%22%2C%22telemetryId%22%3A%2220409084-39a1-4800-bbce-d0b26a6f46a4%22%7D/searchTelemetryId/d7d20e05-ca16-47f7-bed5-9c7b8d2fa641).
### 5. Create the Service Catalog Managed App Definition
In the Azure Portal, go to Marketplace and create a `Service Catalog Managed App Definition`. You must provide a uri link to the uploaded `managed-app.zip` file as part of the creation process.
### 6. Deploy the managed app
In the Azure Portal, find and click on the managed app definition resource that was created in the previous step. A button option to `Deploy from definition` will be available. Click on it and proceed through the setup steps (defined by the `createUiDefinitions.json` file) that a consumer would experience when installing the managed app.

View File

@ -4,17 +4,23 @@ param openAiName string = 'openai${uniqueString(resourceGroup().id)}'
@description('Location for the Azure OpenAI instance') @description('Location for the Azure OpenAI instance')
param location string = resourceGroup().location param location string = resourceGroup().location
@description('LLM model deployment name') @description('LLM model name')
param llmModelDeploymentName string = 'gpt-4o' param llmModelName string = 'gpt-4o'
@description('Embedding model deployment name') @description('LLM Model API version')
param embeddingModelDeploymentName string = 'text-embedding-ada-002' param llmModelVersion string
@description('TPM quota for GPT-4o deployment') @description('Embedding model name')
param gpt4oTpm int = 10 param embeddingModelName string = 'text-embedding-ada-002'
@description('TPM quota for text-embedding-ada-002 deployment') @description('Embedding Model API version')
param textEmbeddingAdaTpm int = 10 param embeddingModelVersion string
@description('TPM quota for llm model deployment (x1000)')
param llmTpmQuota int = 10
@description('TPM quota for embedding model deployment (x1000)')
param embeddingTpmQuota int = 10
@description('Array of objects with fields principalId, roleDefinitionId') @description('Array of objects with fields principalId, roleDefinitionId')
param roleAssignments array = [] param roleAssignments array = []
@ -32,39 +38,39 @@ resource aoai 'Microsoft.CognitiveServices/accounts@2024-10-01' = {
} }
} }
resource gpt4oDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = { resource llmDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = {
parent: aoai parent: aoai
name: llmModelDeploymentName name: llmModelName
sku: { sku: {
name: 'GlobalStandard' name: 'GlobalStandard'
capacity: gpt4oTpm capacity: llmTpmQuota
} }
properties: { properties: {
model: { model: {
format: 'OpenAI' format: 'OpenAI'
name: 'gpt-4o' name: llmModelName
version: '2024-05-13' version: llmModelVersion
} }
currentCapacity: gpt4oTpm currentCapacity: llmTpmQuota
} }
} }
resource textEmbeddingAdaDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = { resource embeddingDeployment 'Microsoft.CognitiveServices/accounts/deployments@2024-10-01' = {
parent: aoai parent: aoai
name: embeddingModelDeploymentName name: embeddingModelName
// NOTE: simultaneous model deployments are not supported at this time. As a workaround, use dependsOn to force the models to be deployed in a sequential manner. // NOTE: simultaneous model deployments are not supported at this time. As a workaround, use dependsOn to force the models to be deployed in a sequential manner.
dependsOn: [gpt4oDeployment] dependsOn: [llmDeployment]
sku: { sku: {
name: 'Standard' name: 'Standard'
capacity: textEmbeddingAdaTpm capacity: embeddingTpmQuota
} }
properties: { properties: {
model: { model: {
format: 'OpenAI' format: 'OpenAI'
name: 'text-embedding-ada-002' name: embeddingModelName
version: '2' version: embeddingModelVersion
} }
currentCapacity: textEmbeddingAdaTpm currentCapacity: embeddingTpmQuota
} }
} }
@ -77,9 +83,9 @@ resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
] ]
output openAiEndpoint string = aoai.properties.endpoint output openAiEndpoint string = aoai.properties.endpoint
output llmModel string = gpt4oDeployment.properties.model.name output llmModel string = llmDeployment.properties.model.name
output llmModelDeploymentName string = gpt4oDeployment.name output llmModelDeploymentName string = llmDeployment.name
output llmModelApiVersion string = gpt4oDeployment.apiVersion output llmModelApiVersion string = llmDeployment.apiVersion
output textEmbeddingModel string = textEmbeddingAdaDeployment.properties.model.name output textEmbeddingModel string = embeddingDeployment.properties.model.name
output textEmbeddingModelDeploymentName string = textEmbeddingAdaDeployment.name output textEmbeddingModelDeploymentName string = embeddingDeployment.name
output textEmbeddingModelApiVersion string = textEmbeddingAdaDeployment.apiVersion output textEmbeddingModelApiVersion string = embeddingDeployment.apiVersion

View File

@ -16,7 +16,6 @@ GRAPHRAG_IMAGE=""
PUBLISHER_EMAIL="" PUBLISHER_EMAIL=""
PUBLISHER_NAME="" PUBLISHER_NAME=""
RESOURCE_BASE_NAME="" RESOURCE_BASE_NAME=""
CONTAINER_REGISTRY_NAME=""
requiredParams=( requiredParams=(
LOCATION LOCATION
@ -316,7 +315,6 @@ deployAzureResources () {
--parameters "apiPublisherName=$PUBLISHER_NAME" \ --parameters "apiPublisherName=$PUBLISHER_NAME" \
--parameters "apiPublisherEmail=$PUBLISHER_EMAIL" \ --parameters "apiPublisherEmail=$PUBLISHER_EMAIL" \
--parameters "enablePrivateEndpoints=$ENABLE_PRIVATE_ENDPOINTS" \ --parameters "enablePrivateEndpoints=$ENABLE_PRIVATE_ENDPOINTS" \
--parameters "acrName=$CONTAINER_REGISTRY_NAME" \
--output json) --output json)
# errors in deployment may not be caught by exitIfCommandFailed function so we also check the output for errors # errors in deployment may not be caught by exitIfCommandFailed function so we also check the output for errors
exitIfCommandFailed $? "Error deploying Azure resources..." exitIfCommandFailed $? "Error deploying Azure resources..."

View File

@ -56,6 +56,24 @@ param storageAccountName string = ''
param cosmosDbName string = '' param cosmosDbName string = ''
param aiSearchName string = '' param aiSearchName string = ''
// AOAI parameters
@description('Name of the AOAI LLM model to use. Must match official model id. For more information: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models')
@allowed(['gpt-4o', 'gpt-4o-mini'])
param llmModelName string = 'gpt-4o'
@description('Version of the AOAI LLM model to use.')
param llmModelVersion string = '2024-08-06'
@description('Quota of the AOAI LLM model to use.')
@minValue(1)
param llmModelQuota int = 10
@description('Name of the AOAI embedding model to use. Must match official model id. For more information: https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models')
@allowed(['text-embedding-ada-002', 'text-embedding-3-large'])
param embeddingModelName string = 'text-embedding-ada-002'
param embeddingModelVersion string = '2'
@description('Quota of the AOAI embedding model to use.')
@minValue(1)
param embeddingModelQuota int = 10
var abbrs = loadJsonContent('abbreviations.json') var abbrs = loadJsonContent('abbreviations.json')
var tags = { 'azd-env-name': resourceGroup } var tags = { 'azd-env-name': resourceGroup }
var workloadIdentityName = '${abbrs.managedIdentityUserAssignedIdentities}${resourceBaseNameFinal}' var workloadIdentityName = '${abbrs.managedIdentityUserAssignedIdentities}${resourceBaseNameFinal}'
@ -175,10 +193,12 @@ module aoai 'core/aoai/aoai.bicep' = {
params: { params: {
openAiName: '${abbrs.cognitiveServicesAccounts}${resourceBaseNameFinal}' openAiName: '${abbrs.cognitiveServicesAccounts}${resourceBaseNameFinal}'
location: location location: location
llmModelDeploymentName: 'gpt-4o-${uniqueString(resourceBaseNameFinal)}' llmModelName: llmModelName
gpt4oTpm: 10 llmModelVersion: llmModelVersion
embeddingModelDeploymentName: 'text-embedding-ada-002-${uniqueString(resourceBaseNameFinal)}' llmTpmQuota: llmModelQuota
textEmbeddingAdaTpm: 10 embeddingModelName: embeddingModelName
embeddingModelVersion: embeddingModelVersion
embeddingTpmQuota: embeddingModelQuota
roleAssignments: [ roleAssignments: [
{ {
principalId: workloadIdentity.outputs.principalId principalId: workloadIdentity.outputs.principalId
@ -419,6 +439,11 @@ output azure_ai_search_name string = aiSearch.outputs.name
output azure_acr_login_server string = acr.outputs.loginServer output azure_acr_login_server string = acr.outputs.loginServer
output azure_acr_name string = acr.outputs.name output azure_acr_name string = acr.outputs.name
output azure_aks_name string = aks.outputs.name
output azure_aks_controlplanefqdn string = aks.outputs.controlPlaneFqdn
output azure_aks_managed_rg string = aks.outputs.managedResourceGroup
output azure_aks_service_account_name string = aksServiceAccountName
output azure_aoai_endpoint string = aoai.outputs.openAiEndpoint output azure_aoai_endpoint string = aoai.outputs.openAiEndpoint
output azure_aoai_llm_model string = aoai.outputs.llmModel output azure_aoai_llm_model string = aoai.outputs.llmModel
output azure_aoai_llm_model_deployment_name string = aoai.outputs.llmModelDeploymentName output azure_aoai_llm_model_deployment_name string = aoai.outputs.llmModelDeploymentName
@ -427,32 +452,27 @@ output azure_aoai_embedding_model string = aoai.outputs.textEmbeddingModel
output azure_aoai_embedding_model_deployment_name string = aoai.outputs.textEmbeddingModelDeploymentName output azure_aoai_embedding_model_deployment_name string = aoai.outputs.textEmbeddingModelDeploymentName
output azure_aoai_embedding_model_api_version string = aoai.outputs.textEmbeddingModelApiVersion output azure_aoai_embedding_model_api_version string = aoai.outputs.textEmbeddingModelApiVersion
output azure_aks_name string = aks.outputs.name output azure_apim_name string = apim.outputs.name
output azure_aks_controlplanefqdn string = aks.outputs.controlPlaneFqdn output azure_apim_gateway_url string = apim.outputs.apimGatewayUrl
output azure_aks_managed_rg string = aks.outputs.managedResourceGroup
output azure_aks_service_account_name string = aksServiceAccountName
output azure_workload_identity_client_id string = workloadIdentity.outputs.clientId output azure_app_hostname string = appHostname
output azure_workload_identity_principal_id string = workloadIdentity.outputs.principalId output azure_app_url string = appUrl
output azure_workload_identity_name string = workloadIdentity.outputs.name
output azure_storage_account string = storage.outputs.name output azure_app_insights_connection_string string = apim.outputs.appInsightsConnectionString
output azure_storage_account_blob_url string = storage.outputs.primaryEndpoints.blob
output azure_cosmosdb_endpoint string = cosmosdb.outputs.endpoint output azure_cosmosdb_endpoint string = cosmosdb.outputs.endpoint
output azure_cosmosdb_name string = cosmosdb.outputs.name output azure_cosmosdb_name string = cosmosdb.outputs.name
output azure_cosmosdb_id string = cosmosdb.outputs.id output azure_cosmosdb_id string = cosmosdb.outputs.id
output azure_app_insights_connection_string string = apim.outputs.appInsightsConnectionString
output azure_apim_name string = apim.outputs.name
output azure_apim_gateway_url string = apim.outputs.apimGatewayUrl
output azure_dns_zone_name string = privateDnsZone.outputs.name output azure_dns_zone_name string = privateDnsZone.outputs.name
output azure_app_hostname string = appHostname
output azure_app_url string = appUrl
output azure_private_dns_zones array = enablePrivateEndpoints output azure_private_dns_zones array = enablePrivateEndpoints
? union(privatelinkPrivateDns.outputs.privateDnsZones, [privateDnsZone.outputs.name]) ? union(privatelinkPrivateDns.outputs.privateDnsZones, [privateDnsZone.outputs.name])
: [] : []
output azure_storage_account string = storage.outputs.name
output azure_storage_account_blob_url string = storage.outputs.primaryEndpoints.blob
output azure_workload_identity_client_id string = workloadIdentity.outputs.clientId
output azure_workload_identity_principal_id string = workloadIdentity.outputs.principalId
output azure_workload_identity_name string = workloadIdentity.outputs.name

View File

@ -0,0 +1,176 @@
{
"$schema": "https://schema.management.azure.com/schemas/0.1.2-preview/CreateUIDefinition.MultiVm.json#",
"handler": "Microsoft.Azure.CreateUIDef",
"version": "0.1.2-preview",
"parameters": {
"basics": [
{}
],
"steps": [
{
"name": "aoaiSettings",
"label": "AOAI Settings",
"subLabel": {
"preValidation": "Configure the AOAI settings",
"postValidation": "Completed"
},
"elements": [
{
"name": "llmModel",
"type": "Microsoft.Common.DropDown",
"label": "LLM Model",
"defaultValue": "gpt-4o",
"toolTip": "LLM model to use.",
"constraints": {
"allowedValues": [
{
"label": "gpt-4o",
"value": "gpt-4o"
},
{
"label": "gpt-4o-mini",
"value": "gpt-4o-mini"
}
],
"required": true
},
"visible": true
},
{
"name": "llmModelVersion",
"type": "Microsoft.Common.DropDown",
"label": "LLM Model Version",
"defaultValue": "2024-08-06",
"toolTip": "LLM model version to use.",
"constraints": {
"allowedValues": [
{
"label": "2024-08-06",
"value": "2024-08-06"
},
{
"label": "2024-07-18",
"value": "2024-07-18"
}
],
"required": true
},
"visible": true
},
{
"name": "llmModelQuota",
"type": "Microsoft.Common.TextBox",
"label": "LLM Model Quota (x1000)",
"placeholder": "85",
"defaultValue": "",
"toolTip": "Model quota to use.",
"constraints": {
"required": true,
"regex": "^[1-9][0-9]*$",
"validationMessage": "Valid LLM model quota."
},
"visible": true
},
{
"name": "embeddingModel",
"type": "Microsoft.Common.DropDown",
"label": "Embedding Model",
"defaultValue": "text-embedding-ada-002",
"toolTip": "Embedding model to use",
"constraints": {
"allowedValues": [
{
"label": "text-embedding-ada-002",
"value": "text-embedding-ada-002"
},
{
"label": "text-embedding-3-large",
"value": "text-embedding-3-large"
}
],
"required": true
},
"visible": true
},
{
"name": "embeddingModelQuota",
"type": "Microsoft.Common.TextBox",
"label": "Embedding Model Quota (x1000)",
"placeholder": "100",
"defaultValue": "",
"toolTip": "Model quota to use.",
"constraints": {
"required": true,
"regex": "^[1-9][0-9]*$",
"validationMessage": "Valid embedding model quota."
},
"visible": true
},
{
"name": "embeddingModelVersion",
"type": "Microsoft.Common.DropDown",
"label": "Embedding Model Version",
"defaultValue": "2",
"toolTip": "Use a valid embedding model version.",
"constraints": {
"allowedValues": [
{
"label": "2",
"value": "2"
},
{
"label": "1",
"value": "1"
}
],
"required": true
},
"visible": true
}
]
},
{
"name": "graphragSettings",
"label": "GraphRAG Settings",
"subLabel": {
"preValidation": "Configure the graphrag settings",
"postValidation": "Completed"
},
"elements": [
{
"name": "apimTier",
"type": "Microsoft.Common.DropDown",
"label": "APIM Tier",
"defaultValue": "StandardV2",
"toolTip": "APIM tier to use",
"constraints": {
"allowedValues": [
{
"label": "Developer",
"value": "Developer"
},
{
"label": "StandardV2",
"value": "StandardV2"
}
],
"required": true
},
"visible": true
}
]
}
],
"outputs": {
"resourceGroup": "[resourceGroup().name]",
"location": "[location()]",
"apimTier": "[steps('graphragSettings').apimTier]",
"llmModelName": "[steps('aoaiSettings').llmModel]",
"llmModelQuota": "[int(steps('aoaiSettings').llmModelQuota)]",
"embeddingModelName": "[steps('aoaiSettings').embeddingModel]",
"embeddingModelQuota": "[int(steps('aoaiSettings').embeddingModelQuota)]",
"llmModelVersion": "[steps('aoaiSettings').llmModelVersion]",
"embeddingModelVersion": "[steps('aoaiSettings').embeddingModelVersion]"
}
}
}