diff --git a/.github/workflows/azure-dev-build-only.yml b/.github/workflows/azure-dev-build-only.yml new file mode 100644 index 00000000..15926842 --- /dev/null +++ b/.github/workflows/azure-dev-build-only.yml @@ -0,0 +1,50 @@ +name: Azure Dev - Build Only + +on: + push: + branches: + - 'feature/*' + pull_request: + branches: + - main + +jobs: + build-test: + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 8.x + + - name: Install local certs + shell: pwsh + run: | + dotnet dev-certs https --clean + dotnet dev-certs https --trust + + - name: Install Aspire workload + shell: pwsh + run: | + dotnet workload update + dotnet workload install aspire + + - name: Restore NuGet packages + shell: bash + run: | + dotnet restore + + - name: Build solution + shell: bash + run: | + dotnet build + + - name: Test solution + shell: bash + run: | + dotnet test diff --git a/.github/workflows/azure-dev.yml b/.github/workflows/azure-dev.yml index f5f0d502..1bbdc574 100644 --- a/.github/workflows/azure-dev.yml +++ b/.github/workflows/azure-dev.yml @@ -1,108 +1,42 @@ +name: Azure Dev + on: workflow_dispatch: push: branches: - main - - 'feature/*' -# https://learn.microsoft.com/en-us/azure/developer/github/connect-from-azure?tabs=azure-portal%2Clinux#set-up-azure-login-with-openid-connect-authentication permissions: id-token: write contents: read jobs: - build: + build-test-deploy: + runs-on: ubuntu-latest + env: + AZURE_CLIENT_ID: ${{ vars.AZURE_CLIENT_ID }} + AZURE_TENANT_ID: ${{ vars.AZURE_TENANT_ID }} + AZURE_SUBSCRIPTION_ID: ${{ vars.AZURE_SUBSCRIPTION_ID }} + AZURE_ENV_NAME: ${{ vars.AZURE_ENV_NAME }} + AZURE_LOCATION: ${{ vars.AZURE_LOCATION }} + steps: - name: Checkout uses: actions/checkout@v4 - # - name: Login to Azure - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # uses: azure/login@v1 - # with: - # tenant-id: ${{ vars.AZURE_TENANT_ID }} - # subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} - # client-id: ${{ vars.AZURE_CLIENT_ID }} - - # # - name: Install Azure Developer CLI - # # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # # uses: Azure/setup-azd@v0.1.0 - - # - name: Install Azure Developer CLI (nightly build) - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # shell: pwsh - # run: | - # Invoke-RestMethod 'https://aka.ms/install-azd.ps1' -OutFile ./install-azd.ps1 - # ./install-azd.ps1 -Version daily - - # - name: Login to Azure Developer CLI - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # shell: pwsh - # run: | - # azd auth login ` - # --tenant-id "${{ vars.AZURE_TENANT_ID }}" ` - # --client-id "${{ vars.AZURE_CLIENT_ID }}" ` - # --federated-credential-provider "${{ vars.AZD_PIPELINE_PROVIDER }}" - - # - name: Setup environment - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # shell: pwsh - # run: | - # # Create config.json under .azure - # New-Item -Type Directory .azure - # $config = @{ version = 1; defaultEnvironment = "${{ vars.AZURE_ENV_NAME }}" } - # $config | ConvertTo-Json -Depth 100 | Out-File -Path ./.azure/config.json -Force - - # # Create config.json under .azure/${{ vars.AZURE_ENV_NAME }} - # New-Item -Type Directory .azure/${{ vars.AZURE_ENV_NAME }} - # $config = @{ services = @{ app = @{ config = @{ exposedServices = @( "playground" ) } } } } - # $config | ConvertTo-Json -Depth 100 | Out-File -Path ./.azure/${{ vars.AZURE_ENV_NAME }}/config.json -Force - - # # Create .env under .azure/${{ vars.AZURE_ENV_NAME }} - # $dotenv = @() - # $dotenv += "AZD_PIPELINE_PROVIDER=`"${{ vars.AZD_PIPELINE_PROVIDER }}`"" - # $dotenv += "AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN=`"${{ vars.AZURE_CONTAINER_APPS_ENVIRONMENT_DEFAULT_DOMAIN }}`"" - # $dotenv += "AZURE_CONTAINER_APPS_ENVIRONMENT_ID=`"${{ vars.AZURE_CONTAINER_APPS_ENVIRONMENT_ID }}`"" - # $dotenv += "AZURE_CONTAINER_REGISTRY_ENDPOINT=`"${{ vars.AZURE_CONTAINER_REGISTRY_ENDPOINT }}`"" - # $dotenv += "AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID=`"${{ vars.AZURE_CONTAINER_REGISTRY_MANAGED_IDENTITY_ID }}`"" - # $dotenv += "AZURE_ENV_NAME=`"${{ vars.AZURE_ENV_NAME }}`"" - # $dotenv += "AZURE_LOCATION=`"${{ vars.AZURE_LOCATION }}`"" - # $dotenv += "AZURE_PIPELINE_CLIENT_ID=`"${{ vars.AZURE_PIPELINE_CLIENT_ID }}`"" - # $dotenv += "AZURE_SUBSCRIPTION_ID=`"${{ vars.AZURE_SUBSCRIPTION_ID }}`"" - # $dotenv += "MANAGED_IDENTITY_CLIENT_ID=`"${{ vars.MANAGED_IDENTITY_CLIENT_ID }}`"" - # $dotenv += "SERVICE_BINDING_TABLE_ENDPOINT=`"${{ vars.SERVICE_BINDING_TABLE_ENDPOINT }}`"" - # $dotenv | Out-File -Path ./.azure/${{ vars.AZURE_ENV_NAME }}/.env -Force - - # - name: Update appsettings.json - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # shell: pwsh - # run: | - # $instances = @() - # $aoais = az resource list -g rg-${{ vars.AZURE_ENV_NAME }} --query "[?type=='Microsoft.CognitiveServices/accounts'].name" | ConvertFrom-Json - # $aoais | ForEach-Object { - # $name = $_ - # $endpoint = az cognitiveservices account show -g rg-${{ vars.AZURE_ENV_NAME }} -n $name --query "properties.endpoint" -o tsv - # $apiKey = az cognitiveservices account keys list -g rg-${{ vars.AZURE_ENV_NAME }} -n $name --query "key1" -o tsv - # $deploymentName = az cognitiveservices account deployment list -g rg-${{ vars.AZURE_ENV_NAME }} -n $name --query "[].name" -o tsv - - # $instance = @{ Endpoint = $endpoint; ApiKey = $apiKey; DeploymentName = $deploymentName } - # $instances += $instance - # } - - # Copy-Item -Path ./src/AzureOpenAIProxy.AppHost/appsettings.Development.sample.json ` - # -Destination ./src/AzureOpenAIProxy.AppHost/appsettings.Development.json -Force - - # $appsettings = Get-Content -Path ./src/AzureOpenAIProxy.AppHost/appsettings.Development.json | ConvertFrom-Json - # $appsettings.AOAI.Instances = $instances - # $appsettings | ConvertTo-Json -Depth 100 | Out-File -Path ./src/AzureOpenAIProxy.AppHost/appsettings.Development.json -Force - - name: Setup .NET SDK uses: actions/setup-dotnet@v4 with: dotnet-version: 8.x + - name: Install local certs + shell: pwsh + run: | + dotnet dev-certs https --clean + dotnet dev-certs https --trust + - name: Install Aspire workload shell: pwsh run: | @@ -123,9 +57,22 @@ jobs: shell: bash run: | dotnet test + + - name: Install azd + uses: Azure/setup-azd@v1.0.0 + + - name: Log in with Azure (Federated Credentials) + run: | + azd auth login ` + --client-id "$Env:AZURE_CLIENT_ID" ` + --federated-credential-provider "github" ` + --tenant-id "$Env:AZURE_TENANT_ID" + shell: pwsh + + - name: Provision Infrastructure + run: azd provision --no-prompt + env: + AZD_INITIAL_ENVIRONMENT_CONFIG: ${{ secrets.AZD_INITIAL_ENVIRONMENT_CONFIG }} - # - name: Deploy to Azure Container Apps - # if: github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch' - # shell: pwsh - # run: | - # azd deploy + - name: Deploy Application + run: azd deploy --no-prompt diff --git a/biceps/openAI.bicep b/biceps/openAI.bicep deleted file mode 100644 index fded685d..00000000 --- a/biceps/openAI.bicep +++ /dev/null @@ -1,69 +0,0 @@ -@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 - -@description('List of tags for Azure resources') -param tags object = { - 'azd-env-name': environmentName -} - -@description('The location where the resources will be deployed') -param location string = resourceGroup().location - -@description('The OpenAI model to deploy') -param model object = { - name: 'model-name' - deploymentName: 'deployment-name' - version: 'version' - skuName: 'SKU' - skuCapacity: 4 -} - -var resourceToken = uniqueString(resourceGroup().id) - -var openai = { - name: 'aoai-${resourceToken}-${model.deploymentName}-${location}' - location: location - tags: tags - skuName: 'S0' - model: model -} - -resource aoai 'Microsoft.CognitiveServices/accounts@2023-05-01' = { - name: openai.name - location: openai.location - kind: 'OpenAI' - tags: openai.tags - sku: { - name: openai.skuName - } - properties: { - customSubDomainName: openai.name - publicNetworkAccess: 'Enabled' - } -} - -resource deployment 'Microsoft.CognitiveServices/accounts/deployments@2023-05-01' = { - name: openai.model.deploymentName - parent: aoai - sku: { - name: openai.model.skuName - capacity: openai.model.skuCapacity - } - properties: { - model: { - format: 'OpenAI' - name: openai.model.name - version: openai.model.version - } - } -} - -output instance object = { - id: aoai.id - name: aoai.name - endpoint: aoai.properties.endpoint - apiKey: listKeys(aoai.id, '2023-05-01').key1 - deploymentName: deployment.name -} diff --git a/biceps/openAIModels.bicep b/biceps/openAIModels.bicep deleted file mode 100644 index 421476d2..00000000 --- a/biceps/openAIModels.bicep +++ /dev/null @@ -1,78 +0,0 @@ -@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 - -var tags = { - 'azd-env-name': environmentName -} - -// GPT3.5 Turbo 16K available regions -var locationsGpt35Turbo16k = [ - 'australiaeast' - 'canadaeast' - 'eastus' - 'eastus2' - 'francecentral' - 'japaneast' - 'northcentralus' -// 'norwayeast' -// 'southindia' - 'swedencentral' - 'switzerlandnorth' - 'uksouth' -// 'westeurope' -// 'westus' -] - -// GPT4 32K available regions -var locationsGpt432k = [ - 'australiaeast' - 'canadaeast' - 'eastus' -// 'eastus2' - 'francecentral' -// 'japaneast' -// 'northcentralus' -// 'norwayeast' -// 'southindia' - 'swedencentral' - 'switzerlandnorth' -// 'uksouth' -// 'westeurope' -// 'westus' -] - -module gpt35Turbo16ks 'openAIs.bicep' = { - name: 'AOAI_GPT35TURBO16K' - params: { - environmentName: environmentName - tags: tags - locations: locationsGpt35Turbo16k - model: { - name: 'gpt-35-turbo-16k' - deploymentName: 'model-gpt35turbo16k' - version: '0613' - skuName: 'Standard' - skuCapacity: 4 - } - } -} - -module gpt432ks 'openAIs.bicep' = { - name: 'AOAI_GPT432K' - params: { - environmentName: environmentName - tags: tags - locations: locationsGpt432k - model: { - name: 'gpt-4-32k' - deploymentName: 'model-gpt432k' - version: '0613' - skuName: 'Standard' - skuCapacity: 4 - } - } -} - -output instances array = concat(gpt35Turbo16ks.outputs.instances, gpt432ks.outputs.instances) diff --git a/biceps/openAIs.bicep b/biceps/openAIs.bicep deleted file mode 100644 index d91e7e8a..00000000 --- a/biceps/openAIs.bicep +++ /dev/null @@ -1,36 +0,0 @@ -@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 - -param tags object = { - 'azd-env-name': environmentName -} - -param locations array = [] - -param model object = { - name: 'model-name' - deploymentName: 'deployment-name' - version: 'version' - skuName: 'SKU' - skuCapacity: 4 -} - -module openAIs 'openAI.bicep' = [for location in locations: { - name: 'AOAI_${model.deploymentName}_${location}' - params: { - environmentName: environmentName - location: location - tags: tags - model: model - } -}] - -output instances array = [for i in range(0, length(locations)): { - id: openAIs[i].outputs.instance.id - name: openAIs[i].outputs.instance.name - endpoint: openAIs[i].outputs.instance.endpoint - apiKey: openAIs[i].outputs.instance.apiKey - deploymentName: model.deploymentName -}] diff --git a/biceps/resourceGroup.bicep b/biceps/resourceGroup.bicep deleted file mode 100644 index 69d38b41..00000000 --- a/biceps/resourceGroup.bicep +++ /dev/null @@ -1 +0,0 @@ -output resourceToken string = uniqueString(resourceGroup().id) diff --git a/infra/scripts/Create-BulkOpenAIs.ps1 b/infra/scripts/Create-BulkOpenAIs.ps1 new file mode 100644 index 00000000..4bba2cf8 --- /dev/null +++ b/infra/scripts/Create-BulkOpenAIs.ps1 @@ -0,0 +1,42 @@ +Param( + [string] + [Parameter(Mandatory=$false)] + $AzureEnvironmentName +) + +$models = @( + @{ + ModelName = "dall-e-3"; + ModelVersion = "3.0"; + Capacity = 1; + }, + @{ + ModelName = "gpt-35-turbo-16k"; + ModelVersion = "0613"; + Capacity = 12; + }, + @{ + ModelName = "gpt-4o"; + ModelVersion = "2024-05-13"; + Capacity = 12; + }, + @{ + ModelName = "text-embedding-ada-002"; + ModelVersion = "2"; + Capacity = 120; + } +) + +$REPOSITORY_ROOT = git rev-parse --show-toplevel + +# $models | ForEach-Object { +# $model = $_ +# & "$REPOSITORY_ROOT/infra/scripts/New-OpenAIs.ps1" -AzureEnvironmentName $AzureEnvironmentName -ModelName "$($model.ModelName)" -ModelVersion "$($model.ModelVersion)" -Capacity $($model.Capacity) +# } + +& "$REPOSITORY_ROOT/infra/scripts/New-OpenAIs.ps1" -AzureEnvironmentName $AZURE_ENV_NAME -ModelName "gpt-35-turbo-16k" -ModelVersion "0613" -Capacity 12 +& "$REPOSITORY_ROOT/infra/scripts/New-OpenAIs.ps1" -AzureEnvironmentName $AZURE_ENV_NAME -ModelName "gpt-4o" -ModelVersion "2024-05-13" -Capacity 12 +& "$REPOSITORY_ROOT/infra/scripts/New-OpenAIs.ps1" -AzureEnvironmentName $AZURE_ENV_NAME -ModelName "text-embedding-ada-002" -ModelVersion "2" -Capacity 120 +& "$REPOSITORY_ROOT/infra/scripts/New-OpenAIs.ps1" -AzureEnvironmentName $AZURE_ENV_NAME -ModelName "dall-e-3" -ModelVersion "3.0" -Capacity 1 + +& "$REPOSITORY_ROOT/infra/scripts/Get-OpenAIDetails.ps1" -AzureEnvironmentName $AZURE_ENV_NAME diff --git a/biceps/Get-OpenAIDetails.ps1 b/infra/scripts/Get-OpenAIDetails.ps1 similarity index 76% rename from biceps/Get-OpenAIDetails.ps1 rename to infra/scripts/Get-OpenAIDetails.ps1 index 6a27fe24..e53b85c0 100644 --- a/biceps/Get-OpenAIDetails.ps1 +++ b/infra/scripts/Get-OpenAIDetails.ps1 @@ -2,7 +2,7 @@ Param( [string] [Parameter(Mandatory=$false)] - $EnvironmentName = $null, + $AzureEnvironmentName = $null, [switch] [Parameter(Mandatory=$false)] @@ -13,12 +13,12 @@ function Show-Usage { Write-Output " This gets list of the Azure OpenAI instances with specific deployment name Usage: $(Split-Path $MyInvocation.ScriptName -Leaf) `` - [-EnvironmentName ] `` + [-AzureEnvironmentName ] `` [-Help] Options: - -EnvironmentName Azure environment name. + -AzureEnvironmentName Azure environment name. -Help: Show this message. " @@ -33,12 +33,13 @@ if ($needHelp -eq $true) { Exit 0 } -if ($EnvironmentName -eq $null) { +if ($AzureEnvironmentName -eq $null) { Show-Usage Exit 0 } -$rg = "rg-$EnvironmentName" +$rg = "rg-$AzureEnvironmentName" +$repositoryRoot = git rev-parse --show-toplevel $openAIs = az resource list -g $rg --query "[?type=='Microsoft.CognitiveServices/accounts'].name" | ConvertFrom-Json | Sort-Object @@ -56,4 +57,4 @@ $openAIs | ForEach-Object { $instances += $instance } -$instances | ConvertTo-Json -Depth 100 | Out-File -FilePath "./biceps/instances.json" -Encoding utf8 -Force +$instances | ConvertTo-Json -Depth 100 | Out-File -FilePath "$repositoryRoot/infra/instances.json" -Encoding utf8 -Force diff --git a/biceps/Get-OpenAILocations.ps1 b/infra/scripts/Get-OpenAILocations.ps1 similarity index 100% rename from biceps/Get-OpenAILocations.ps1 rename to infra/scripts/Get-OpenAILocations.ps1 diff --git a/infra/scripts/New-OpenAIs.ps1 b/infra/scripts/New-OpenAIs.ps1 new file mode 100644 index 00000000..76d42576 --- /dev/null +++ b/infra/scripts/New-OpenAIs.ps1 @@ -0,0 +1,161 @@ +# Provisions Azure OpenAI Service instances to all available regions and deploy models on repective locations +Param( + [string] + [Parameter(Mandatory=$false)] + $ResourceGroupLocation = "koreacentral", + + [string] + [Parameter(Mandatory=$false)] + $AzureEnvironmentName, + + [string] + [Parameter(Mandatory=$false)] + $ModelName = "gpt-4o", + + [string] + [Parameter(Mandatory=$false)] + $ModelVersion = "2024-05-13", + + [int] + [Parameter(Mandatory=$false)] + $Capacity = 80, + + [string] + [Parameter(Mandatory=$false)] + $ApiVersion = "2023-05-01", + + [switch] + [Parameter(Mandatory=$false)] + $Help +) + +function Show-Usage { + Write-Host " This provisions Azure OpenAI Service instances to all available regions and deploy models on repective locations + + Usage: $(Split-Path $MyInvocation.ScriptName -Leaf) `` + [-ResourceGroupLocation ] `` + [-AzureEnvironmentName ] `` + [-ModelName ] `` + [-ModelVersion ] `` + [-Capacity ] `` + [-ApiVersion ] `` + + [-Help] + + Options: + -ResourceGroupLocation Resource group name. Default value is `'koreacentral`' + -AzureEnvironmentName Azure eovironment name. + -ModelName Azure OpenAI model name. Default value is `'gpt-4o`' + -ModelVersion Azure OpenAI model version. Default value is `'2024-05-13`' + -Capacity Azure OpenAI model capacity. Default value is `'80`' + -ApiVersion API version. Default value is `'2023-05-01`' + + -Help: Show this message. +" + + Exit 0 +} + +# Show usage +$needHelp = $Help -eq $true +if ($needHelp -eq $true) { + Show-Usage + Exit 0 +} + +if (($ResourceGroupLocation -eq $null) -or ($AzureEnvironmentName -eq $null) -or ($ModelName -eq $null) -or ($ModelVersion -eq $null) -or ($ApiVersion -eq $null)) { + Show-Usage + Exit 0 +} + +# Get resource token +$token = "abcdefghijklmnopqrstuvwxyz" +$subscriptionId = az account show --query "id" -o tsv +$baseValue = "$AzureEnvironmentName|$subscriptionId" +$hasher = [System.Security.Cryptography.HashAlgorithm]::Create('sha256') +$hash = $hasher.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($baseValue)) +$hash | ForEach-Object { $calculated += $token[$_ % 26] } +$resourceToken = $($calculated).Substring(0, 13).ToLowerInvariant() + +# Provision resource group +$resourceGroupName = "rg-$AzureEnvironmentName" + +$resourceGroupExists = az group exists -n $resourceGroupName +if ($resourceGroupExists -eq $false) { + $rg = az group create -n $resourceGroupName -l $ResourceGroupLocation +} + +Write-Host "Provisioning $ModelName ..." -ForegroundColor Yellow + +# Check available locations +$subscriptionId = az account show --query "id" -o tsv +$url = "/subscriptions/$subscriptionId/providers/Microsoft.CognitiveServices" + +$locations = az rest --method GET ` + --uri "$url/skus?api-version=$ApiVersion" ` + --query "value[?kind=='OpenAI'] | [?resourceType == 'accounts'].locations[0]" | ConvertFrom-Json + +$locations | ForEach-Object { + $location = $_.ToLowerInvariant() + + # Check available models + $models = az rest --method GET ` + --uri "$url/locations/$location/models?api-version=$ApiVersion" ` + --query "sort_by(value[?kind == 'OpenAI'].{ name: model.name, version: model.version, skus: model.skus }, &name)" | ConvertFrom-Json + + $model = $models | Where-Object { $_.name -eq $ModelName -and $_.version -eq $ModelVersion -and $_.skus.Count -gt 0 -and $_.skus[0].name -eq "Standard" } + if ($model -ne $null) { + # Temporary exclusion + if ($location -eq "switzerlandnorth") { + continue + } + + # Provision Azure OpenAI Services + $cogsvc = az cognitiveservices account list -g $ResourceGroupName --query "[?location=='$location']" | ConvertFrom-Json + if ($cogsvc -eq $null) { + $resourceName = "cogsvc-$resourceToken-$location" + + Write-Host "Provisioning $resourceName instance ..." -ForegroundColor Cyan + + $cogsvc = az cognitiveservices account create ` + -g $resourceGroupName ` + -n $resourceName ` + -l $location ` + --kind OpenAI ` + --sku S0 ` + --assign-identity ` + --tags azd-env-name=cogsvc-$AzureEnvironmentName | ConvertFrom-Json + + Write-Host " $resourceName instance has been provisioned" -ForegroundColor Cyan + } + + $modelNameShortened = $ModelName.Replace("-", "").Replace(".", "") + $modelVersionShortened = $ModelVersion.Replace("-", "").Replace(".", "") + $deploymentName = "model-$modelNameShortened-$modelVersionShortened" + $skuName = ($ModelName -eq "gpt-4o") ? "GlobalStandard" : "Standard" + + $deployment = az cognitiveservices account deployment list ` + -g $resourceGroupName ` + -n "cogsvc-$resourceToken-$location" ` + --query "[?name=='$deploymentName']" | ConvertFrom-Json + + # Deploy model + if ($deployment -eq $null) { + Write-Host "Provisioning $deploymentName on the $($cogsvc.name) instance ..." -ForegroundColor Magenta + + $deployment = az cognitiveservices account deployment create ` + -g $resourceGroupName ` + -n "cogsvc-$resourceToken-$location" ` + --model-format OpenAI ` + --model-name $ModelName ` + --model-version $ModelVersion ` + --deployment-name $deploymentName ` + --sku-name $skuName ` + --sku-capacity $Capacity + + Write-Host " $deploymentName on the $($cogsvc.name) instance has been deployed" -ForegroundColor Magenta + } + } +} + +Write-Host "$ModelName has been provisioned" -ForegroundColor Yellow diff --git a/infra/scripts/Purge-ApiManagement.ps1 b/infra/scripts/Purge-ApiManagement.ps1 new file mode 100644 index 00000000..bfd59e9f --- /dev/null +++ b/infra/scripts/Purge-ApiManagement.ps1 @@ -0,0 +1,132 @@ +# Purges the deleted the API Management instance. +Param( + [string] + [Parameter(Mandatory=$false)] + $ApiVersion = "2021-08-01", + + [switch] + [Parameter(Mandatory=$false)] + $Help +) + +function Show-Usage { + Write-Output " This permanently deletes the API Management instance + + Usage: $(Split-Path $MyInvocation.ScriptName -Leaf) `` + [-ApiVersion ] `` + + [-Help] + + Options: + -ApiVersion REST API version. Default is `2021-08-01`. + + -Help: Show this message. +" + + Exit 0 +} + +# Show usage +$needHelp = $Help -eq $true +if ($needHelp -eq $true) { + Show-Usage + Exit 0 +} + +# List soft-deleted API Management instances +function List-DeletedAPIMs { + param ( + [string] $ApiVersion + ) + + $account = $(az account show | ConvertFrom-Json) + + $url = "https://management.azure.com/subscriptions/$($account.id)/providers/Microsoft.ApiManagement/deletedservices?api-version=$($ApiVersion)" + + # Uncomment to debug + # $url + + $options = "" + + $apims = $(az rest -m get -u $url --query "value" | ConvertFrom-Json) + if ($apims -eq $null) { + $options = "All soft-deleted API Management instances purged or no such instance found to purge" + $returnValue = @{ apims = $apims; options = $options } + return $returnValue + } + + if ($apims.Count -eq 1) { + $name = $apims.name + $options += " 1: $name `n" + } else { + $apims | ForEach-Object { + $i = $apims.IndexOf($_) + $name = $_.name + $options += " $($i +1): $name `n" + } + } + $options += " q: Quit`n" + + $returnValue = @{ apims = $apims; options = $options } + return $returnValue +} + +# Purge soft-deleted API Management instances +function Purge-DeletedAPIMs { + param ( + [string] $ApiVersion + ) + + $continue = $true + $result = List-DeletedAPIMs -ApiVersion $ApiVersion + if ($result.apims -eq $null) { + $continue = $false + } + + while ($continue -eq $true) { + $options = $result.options + + $input = Read-Host "Select the number to purge the soft-deleted API Management instance or 'q' to quit: `n`n$options" + if ($input -eq "q") { + $continue = $false + break + } + + $parsed = $input -as [int] + if ($parsed -eq $null) { + Write-Output "Invalid input" + $continue = $false + break + } + + $apims = $result.apims + if ($parsed -gt $apims.Count) { + Write-Output "Invalid input" + $continue = $false + break + } + + $index = $parsed - 1 + + $url = "https://management.azure.com$($apims[$index].id)?api-version=$($ApiVersion)" + + # Uncomment to debug + # $url + + $apim = $(az rest -m get -u $url) + if ($apim -ne $null) { + $deleted = $(az rest -m delete -u $url) + } + + $result = List-DeletedAPIMs -ApiVersion $ApiVersion + if ($result.apims -eq $null) { + $continue = $false + } + } + + if ($continue -eq $false) { + return $result.options + } +} + +Purge-DeletedAPIMs -ApiVersion $ApiVersion diff --git a/biceps/Purge-CognitiveServices.ps1 b/infra/scripts/Purge-CognitiveServices.ps1 similarity index 100% rename from biceps/Purge-CognitiveServices.ps1 rename to infra/scripts/Purge-CognitiveServices.ps1 diff --git a/biceps/Run-PostProvision.ps1 b/infra/scripts/Run-PostProvision.ps1 similarity index 100% rename from biceps/Run-PostProvision.ps1 rename to infra/scripts/Run-PostProvision.ps1 diff --git a/biceps/Set-GitHubActionsVariables.ps1 b/infra/scripts/Set-GitHubActionsVariables.ps1 similarity index 100% rename from biceps/Set-GitHubActionsVariables.ps1 rename to infra/scripts/Set-GitHubActionsVariables.ps1