From a6c94ed10b5347829a1bf671593de6f8056d30b6 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Thu, 15 May 2025 12:55:13 -0700 Subject: [PATCH 01/14] Add new Flex with UAMI script + snippet source file --- ...reate-function-app-flex-plan-identities.sh | 66 +++++++++++++++++++ ...eate-function-app-flex-plan-identities.txt | 48 ++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh create mode 100644 azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh new file mode 100644 index 00000000..5b7370b0 --- /dev/null +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Passed validation in Cloud Shell on 3/24/2022 + +# +# Function app, storage account, and user identity names must be unique. + +# Variable block +let "randomIdentifier=$RANDOM*$RANDOM" +location="northeurope" +resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" +tag="create-function-app-flex-plan-identities" +storage="msdocsaccount$randomIdentifier" +userIdentity="msdocs-managed-identity-$randomIdentifier" +functionApp="msdocs-serverless-function-$randomIdentifier" +skuStorage="Standard_LRS" +functionsVersion="4" +languageWorker="dotnet-isolated" +languageVersion="8.0" + +# Install the Application Insights extension +az extension add --name application-insights + +# Create a resource group +echo "Creating $resourceGroup in "$location"..." +az group create --name $resourceGroup --location "$location" --tags $tag + +# Create an Azure storage account in the resource group with key access disabled. +echo "Creating $storage" +az storage account create --name $storage --location "$location" --resource-group $resourceGroup \ + --sku $skuStorage --allow-blob-public-access false --allow-shared-key-access false + +# Create a user-assigned managed identity +echo "Creating $userIdentity" +output=$(az identity create --name $userIdentity --resource-group $resourceGroup --location $location \ + --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) + +# Use jq to parse the output and assign the properties to variables +userId=$(echo $output | jq -r '.userId') +principalId=$(echo $output | jq -r '.principalId') +clientId=$(echo $output | jq -r '.clientId') + +# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the user +storageId=$(az storage account show --resource-group $resourceGroup --name $storage --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $storageId + +# Create the function app in a Flex Consumption plan that uses the user-assigned managed identity +# to access the deployment share. +az functionapp create --resource-group $resourceGroup --name $functionApp --flexconsumption-location $location \ + --runtime $languageWorker --runtime-version $languageVersion --storage-account $storage \ + --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value $userIdentity + +# Create a role assigment (Monitoring Metrics Publisher) in Application Insights for the user identity +appInsights=$(az monitor app-insights component show --resource-group $resourceGroup \ + --app $functionApp --query "id" --output tsv) +az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights + +# Update app settings to use managed identities for all connections +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ + AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp --resource-group $resourceGroup --setting-names AzureWebJobsStorage +# + +# echo "Deleting all resources" +# az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt new file mode 100644 index 00000000..55132c74 --- /dev/null +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt @@ -0,0 +1,48 @@ +# This version of create-function-app-flex-plan-identities is maintained for +# hosting code snippets in the Functions Flex CLI quickstarts. +# Date: -5/15/2025 +# Version: 1.0 + +# Install the Application Insights extension +az extension add --name application-insights + +# Create a resource group +az group create --name "AzureFunctionsQuickstart-rg" --location "" --tags $tag + +# Create an Azure storage account in the resource group with key access disabled. +az storage account create --name --location "" --resource-group "AzureFunctionsQuickstart-rg" \ + --sku "Standard_LRS" --allow-blob-public-access false --allow-shared-key-access false + +# Create a user-assigned managed identity +output=$(az identity create --name --resource-group "AzureFunctionsQuickstart-rg" --location \ + --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) + +# Use jq to parse the output and assign the properties to variables +userId=$(echo $output | jq -r '.userId') +principalId=$(echo $output | jq -r '.principalId') +clientId=$(echo $output | jq -r '.clientId') + +# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the user +storageId=$(az storage account show --resource-group "AzureFunctionsQuickstart-rg" --name --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $storageId + +# Create the function app in a Flex Consumption plan that uses the user-assigned managed identity +# to access the deployment share. +az functionapp create --resource-group "AzureFunctionsQuickstart-rg" --name --flexconsumption-location \ + --runtime --runtime-version --storage-account \ + --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value + +# Create a role assignment (Monitoring Metrics Publisher) in Application Insights for the user identity +appInsights=$(az monitor app-insights component show --resource-group "AzureFunctionsQuickstart-rg" \ + --app --query "id" --output tsv) +az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights + +# Update app settings to use managed identities for all connections +az functionapp config appsettings set --name --resource-group "AzureFunctionsQuickstart-rg" \ + --settings AzureWebJobsStorage__accountName= AzureWebJobsStorage__credential=managedidentity \ + AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name --resource-group "AzureFunctionsQuickstart-rg" --setting-names AzureWebJobsStorage + +# echo "Deleting all resources" +# az group delete --name "AzureFunctionsQuickstart-rg" -y From b6d4bbd4f0a6229b6cbe197890195a1e59a1d1ac Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Thu, 15 May 2025 12:58:10 -0700 Subject: [PATCH 02/14] Changed the snippet source to markdown --- ...=> create-function-app-flex-plan-identities.md} | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) rename azure-functions/create-function-app-flex-plan-identities/{create-function-app-flex-plan-identities.txt => create-function-app-flex-plan-identities.md} (91%) diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md similarity index 91% rename from azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt rename to azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md index 55132c74..1105aebd 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.txt +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md @@ -1,8 +1,13 @@ -# This version of create-function-app-flex-plan-identities is maintained for -# hosting code snippets in the Functions Flex CLI quickstarts. -# Date: -5/15/2025 -# Version: 1.0 +--- +Info: This version of create-function-app-flex-plan-identities is maintained for hosting code snippets in the Functions Flex CLI quickstarts. +Date: 05/15/2025 +Version: 1.0 +AI-assisted: true +--- +# Snippet source for create-function-app-flex-plan-identities + +```bash # Install the Application Insights extension az extension add --name application-insights @@ -46,3 +51,4 @@ az functionapp config appsettings delete --name --resource-group "Azu # echo "Deleting all resources" # az group delete --name "AzureFunctionsQuickstart-rg" -y +``` \ No newline at end of file From e60b4ba31323e48fc050e4fef16f305a5f0a175b Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Thu, 15 May 2025 13:07:09 -0700 Subject: [PATCH 03/14] Update test date --- .../create-function-app-flex-plan-identities.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh index 5b7370b0..32435ac1 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# Passed validation in Cloud Shell on 5/15/2025 # # Function app, storage account, and user identity names must be unique. From 566926348786d594aaa655b8d6d8fb26f75ee27b Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Thu, 15 May 2025 15:22:29 -0700 Subject: [PATCH 04/14] add new call to get clientID --- .../create-function-app-flex-plan-identities.md | 1 + .../create-function-app-flex-plan-identities.sh | 1 + 2 files changed, 2 insertions(+) diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md index 1105aebd..48563748 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md @@ -44,6 +44,7 @@ appInsights=$(az monitor app-insights component show --resource-group "AzureFunc az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights # Update app settings to use managed identities for all connections +clientId=$(az identity show --name func-host-storage-user --resource-group AzureFunctionsQuickstart-rg --query 'clientId' -o tsv) az functionapp config appsettings set --name --resource-group "AzureFunctionsQuickstart-rg" \ --settings AzureWebJobsStorage__accountName= AzureWebJobsStorage__credential=managedidentity \ AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh index 32435ac1..116087a7 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh @@ -56,6 +56,7 @@ appInsights=$(az monitor app-insights component show --resource-group $resourceG az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights # Update app settings to use managed identities for all connections +clientId=$(az identity show --name func-host-storage-user --resource-group $group --query 'clientId' -o tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" From ff972f99587e45ab247b4a079c8971b71127be1d Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Fri, 16 May 2025 01:05:49 -0700 Subject: [PATCH 05/14] Add line break --- .../create-function-app-flex-plan-identities.md | 3 ++- .../create-function-app-flex-plan-identities.sh | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md index 48563748..a98a5256 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md @@ -44,7 +44,8 @@ appInsights=$(az monitor app-insights component show --resource-group "AzureFunc az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights # Update app settings to use managed identities for all connections -clientId=$(az identity show --name func-host-storage-user --resource-group AzureFunctionsQuickstart-rg --query 'clientId' -o tsv) +clientId=$(az identity show --name func-host-storage-user --resource-group AzureFunctionsQuickstart-rg \ + --query 'clientId' -o tsv) az functionapp config appsettings set --name --resource-group "AzureFunctionsQuickstart-rg" \ --settings AzureWebJobsStorage__accountName= AzureWebJobsStorage__credential=managedidentity \ AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh index 116087a7..0fa694bb 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh @@ -56,7 +56,8 @@ appInsights=$(az monitor app-insights component show --resource-group $resourceG az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights # Update app settings to use managed identities for all connections -clientId=$(az identity show --name func-host-storage-user --resource-group $group --query 'clientId' -o tsv) +clientId=$(az identity show --name func-host-storage-user --resource-group $group \ + --query 'clientId' -o tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" From 73e11149e91405177fed5deaf0a9b38410d24839 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Fri, 27 Feb 2026 10:58:42 -0800 Subject: [PATCH 06/14] Add new script for creating Azure OpenAI resources and update existing function app script --- .../connect-azure-openai-resources.sh | 106 ++++++++++++++++++ ...reate-function-app-flex-plan-identities.md | 56 --------- ...reate-function-app-flex-plan-identities.sh | 6 +- 3 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh delete mode 100644 azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md diff --git a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh new file mode 100644 index 00000000..0702465e --- /dev/null +++ b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh @@ -0,0 +1,106 @@ +#!/bin/bash +# Passed validation in Cloud Shell on n/y + +# +# Function app, storage account, and user identity names must be unique. + +# Variable block +let "randomIdentifier=$RANDOM*$RANDOM" +location="northeurope" +resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" +tag="create-function-app-flex-plan-identities" +storage="msdocsaccount$randomIdentifier" +userIdentity="msdocs-managed-identity-$randomIdentifier" +functionApp="msdocs-serverless-function-$randomIdentifier" +openaiName="msdocs-openai-$randomIdentifier" +modelName="gpt-4o" +skuStorage="Standard_LRS" +functionsVersion="4" +languageWorker="dotnet-isolated" # Supported values: dotnet-isolated, node, python, powershell, java +languageVersion="8.0" # Supported values: 3.10, 3.11, 7.4, 8.0, 9.0, 10, 11, 17, 20, 21, 22 + +# Install the Application Insights extension +az extension add --name application-insights + +# Create a resource group +echo "Creating $resourceGroup in "$location"..." +az group create --name $resourceGroup --location "$location" --tags $tag + +# Create an Azure storage account in the resource group with key access disabled. +echo "Creating $storage" +az storage account create --name $storage --location "$location" --resource-group $resourceGroup \ + --sku $skuStorage --allow-blob-public-access false --allow-shared-key-access false + +# Create a user-assigned managed identity +echo "Creating $userIdentity" +output=$(az identity create --name $userIdentity --resource-group $resourceGroup --location $location \ + --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) + +# Use jq to parse the output and assign the properties to variables +userId=$(echo $output | jq -r '.userId') +principalId=$(echo $output | jq -r '.principalId') +clientId=$(echo $output | jq -r '.clientId') + +# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the user +storageId=$(az storage account show --resource-group $resourceGroup --name $storage --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $storageId + +# Create the function app in a Flex Consumption plan that uses the user-assigned managed identity +# to access the deployment share. +az functionapp create --resource-group $resourceGroup --name $functionApp --flexconsumption-location $location \ + --runtime $languageWorker --runtime-version $languageVersion --storage-account $storage \ + --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value $userIdentity + +# Create a role assigment (Monitoring Metrics Publisher) in Application Insights for the user identity +appInsights=$(az monitor app-insights component show --resource-group $resourceGroup \ + --app $functionApp --query "id" --output tsv) +az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights + +# Update app settings to use managed identities for all connections +clientId=$(az identity show --name func-host-storage-user --resource-group $group \ + --query 'clientId' -o tsv) +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ + AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp --resource-group $resourceGroup --setting-names AzureWebJobsStorage + +# Create an Azure OpenAI resource +echo "Creating Azure OpenAI resource" +openaiId=$(az cognitiveservices account create --name "msdocs-openai-$randomIdentifier" \ + --resource-group $resourceGroup --kind OpenAI --sku S0 --location $location --yes \ + --query 'id' -o tsv) + +# Create role assignments ("Cognitive Services OpenAI User" & "Azure AI User") for the identity +echo "Adding UAMI to the 'Cognitive Services OpenAI User' role." +principalId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ + --query 'principalId' -o tsv) +az role assignment create --assignee $principalId \ + --role "Cognitive Services OpenAI User" --scope $openaiId +az role assignment create --assignee $principalId \ + --role "Azure AI User" --scope $openaiId + +# Create the same role assignments for your Azure account so you can connect during local development. +echo "Adding current Azure account to the 'Cognitive Services OpenAI User' role." +accountId=$(az ad signed-in-user show --query id -o tsv) +az role assignment create --assignee $accountId \ + --role "Cognitive Services OpenAI User" --scope $openaiId +az role assignment create --assignee $accountId \ + --role "Azure AI User" --scope $openaiId + +# Get the user-assigned managed identity details +user=$(az identity show --name $userIdentity --resource-group $resourceGroup \ + --query "{userId:id, clientId: clientId}" -o json) + +# Add the required app settings to the function app +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings AzureOpenAI__Endpoint="$openaiName.openai.azure.com/" \ + AzureOpenAI__credential=managedidentity \ + AzureOpenAI__managedIdentityResourceId=$(echo $user | jq -r '.userId') \ + AzureOpenAI__clientId=$(echo $user | jq -r '.clientId') \ + CHAT_MODEL_DEPLOYMENT_NAME=$modelName + +# + +# echo "Deleting all resources" +# az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md deleted file mode 100644 index a98a5256..00000000 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -Info: This version of create-function-app-flex-plan-identities is maintained for hosting code snippets in the Functions Flex CLI quickstarts. -Date: 05/15/2025 -Version: 1.0 -AI-assisted: true ---- - -# Snippet source for create-function-app-flex-plan-identities - -```bash -# Install the Application Insights extension -az extension add --name application-insights - -# Create a resource group -az group create --name "AzureFunctionsQuickstart-rg" --location "" --tags $tag - -# Create an Azure storage account in the resource group with key access disabled. -az storage account create --name --location "" --resource-group "AzureFunctionsQuickstart-rg" \ - --sku "Standard_LRS" --allow-blob-public-access false --allow-shared-key-access false - -# Create a user-assigned managed identity -output=$(az identity create --name --resource-group "AzureFunctionsQuickstart-rg" --location \ - --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) - -# Use jq to parse the output and assign the properties to variables -userId=$(echo $output | jq -r '.userId') -principalId=$(echo $output | jq -r '.principalId') -clientId=$(echo $output | jq -r '.clientId') - -# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the user -storageId=$(az storage account show --resource-group "AzureFunctionsQuickstart-rg" --name --query 'id' -o tsv) -az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ - --role "Storage Blob Data Owner" --scope $storageId - -# Create the function app in a Flex Consumption plan that uses the user-assigned managed identity -# to access the deployment share. -az functionapp create --resource-group "AzureFunctionsQuickstart-rg" --name --flexconsumption-location \ - --runtime --runtime-version --storage-account \ - --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value - -# Create a role assignment (Monitoring Metrics Publisher) in Application Insights for the user identity -appInsights=$(az monitor app-insights component show --resource-group "AzureFunctionsQuickstart-rg" \ - --app --query "id" --output tsv) -az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights - -# Update app settings to use managed identities for all connections -clientId=$(az identity show --name func-host-storage-user --resource-group AzureFunctionsQuickstart-rg \ - --query 'clientId' -o tsv) -az functionapp config appsettings set --name --resource-group "AzureFunctionsQuickstart-rg" \ - --settings AzureWebJobsStorage__accountName= AzureWebJobsStorage__credential=managedidentity \ - AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" -az functionapp config appsettings delete --name --resource-group "AzureFunctionsQuickstart-rg" --setting-names AzureWebJobsStorage - -# echo "Deleting all resources" -# az group delete --name "AzureFunctionsQuickstart-rg" -y -``` \ No newline at end of file diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh index 0fa694bb..eeb03d9a 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh +++ b/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh @@ -14,8 +14,8 @@ userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" -languageWorker="dotnet-isolated" -languageVersion="8.0" +languageWorker="dotnet-isolated" # Supported values: dotnet-isolated, node, python, powershell, java +languageVersion="8.0" # Supported values: 3.10, 3.11, 7.4, 8.0, 9.0, 10, 11, 17, 20, 21, 22 # Install the Application Insights extension az extension add --name application-insights @@ -56,7 +56,7 @@ appInsights=$(az monitor app-insights component show --resource-group $resourceG az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights # Update app settings to use managed identities for all connections -clientId=$(az identity show --name func-host-storage-user --resource-group $group \ +clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ --query 'clientId' -o tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ From 9d7dfd3df0e384ca1ca5f3b416abc8b839216d7a Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Fri, 27 Feb 2026 11:11:31 -0800 Subject: [PATCH 07/14] scripts update and cleanup --- .../connect-azure-openai-resources.sh | 10 +++++----- .../create-function-app-app-service-plan.sh | 2 +- .../create-function-app-connect-to-cosmos-db.sh | 2 +- .../create-function-app-consumption-python.sh | 2 +- ...loy-function-app-with-function-github-continuous.sh | 6 +++++- .../functions-cli-mount-files-storage-linux.sh | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh index 0702465e..c293c227 100644 --- a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh +++ b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Passed validation in Cloud Shell on n/y +# TODO: Validate in Cloud Shell before merging # # Function app, storage account, and user identity names must be unique. @@ -8,7 +8,7 @@ let "randomIdentifier=$RANDOM*$RANDOM" location="northeurope" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" -tag="create-function-app-flex-plan-identities" +tag="connect-azure-openai-resources" storage="msdocsaccount$randomIdentifier" userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" @@ -58,7 +58,7 @@ appInsights=$(az monitor app-insights component show --resource-group $resourceG az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights # Update app settings to use managed identities for all connections -clientId=$(az identity show --name func-host-storage-user --resource-group $group \ +clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ --query 'clientId' -o tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ @@ -67,7 +67,7 @@ az functionapp config appsettings delete --name $functionApp --resource-group $r # Create an Azure OpenAI resource echo "Creating Azure OpenAI resource" -openaiId=$(az cognitiveservices account create --name "msdocs-openai-$randomIdentifier" \ +openaiId=$(az cognitiveservices account create --name $openaiName \ --resource-group $resourceGroup --kind OpenAI --sku S0 --location $location --yes \ --query 'id' -o tsv) @@ -94,7 +94,7 @@ user=$(az identity show --name $userIdentity --resource-group $resourceGroup \ # Add the required app settings to the function app az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ - --settings AzureOpenAI__Endpoint="$openaiName.openai.azure.com/" \ + --settings AzureOpenAI__Endpoint="https://$openaiName.openai.azure.com/" \ AzureOpenAI__credential=managedidentity \ AzureOpenAI__managedIdentityResourceId=$(echo $user | jq -r '.userId') \ AzureOpenAI__clientId=$(echo $user | jq -r '.clientId') \ diff --git a/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh b/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh index 505a63d5..ae08dbb4 100644 --- a/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh +++ b/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh @@ -8,7 +8,7 @@ let "randomIdentifier=$RANDOM*$RANDOM" location="eastus" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" -tag="create-function-app-consumption" +tag="create-function-app-app-service-plan" storage="msdocsaccount$randomIdentifier" appServicePlan="msdocs-app-service-plan-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" diff --git a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh index a070de0b..0140662d 100644 --- a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh +++ b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh @@ -39,7 +39,7 @@ key=$(az cosmosdb keys list --name $functionApp --resource-group $resourceGroup echo $key # Configure function app settings to use the Azure Cosmos DB connection string. -az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup --setting CosmosDB_Endpoint=$endpoint CosmosDB_Key=$key +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup --settings CosmosDB_Endpoint=$endpoint CosmosDB_Key=$key # # echo "Deleting all resources" diff --git a/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh b/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh index e3615976..40b9b267 100644 --- a/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh +++ b/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh @@ -13,7 +13,7 @@ storage="msdocsaccount$randomIdentifier" functionApp="msdocs-serverless-python-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" -pythonVersion="3.9" #Allowed values: 3.7, 3.8, and 3.9 +pythonVersion="3.11" #Allowed values: 3.10, 3.11 # Create a resource group echo "Creating $resourceGroup in "$location"..." diff --git a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh index f5c08e08..090ad706 100644 --- a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh +++ b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh @@ -13,6 +13,7 @@ skuStorage="Standard_LRS" functionApp=mygithubfunc$randomIdentifier functionsVersion="4" runtime="node" +runtimeVersion="20" # Public GitHub repository containing an Azure Functions code project. gitrepo=https://github.com/Azure-Samples/functions-quickstart-javascript ## Enable authenticated git deployment in your subscription when using a private repo. @@ -30,7 +31,10 @@ az storage account create --name $storage --location "$location" --resource-grou # Create a function app with source files deployed from the specified GitHub repo. echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $storage --consumption-plan-location "$location" --resource-group $resourceGroup --deployment-source-url $gitrepo --deployment-source-branch main --functions-version $functionsVersion --runtime $runtime +az functionapp create --name $functionApp --storage-account $storage \ + --consumption-plan-location "$location" --resource-group $resourceGroup \ + --deployment-source-url $gitrepo --deployment-source-branch main \ + --functions-version $functionsVersion --runtime $runtime --runtime-version $runtimeVersion # Connect to function application curl -s "https://${functionApp}.azurewebsites.net/api/httpexample?name=Azure" diff --git a/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh b/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh index 5c9384d7..a5fecf77 100644 --- a/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh +++ b/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh @@ -13,7 +13,7 @@ export AZURE_STORAGE_ACCOUNT="msdocsstorage$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" -pythonVersion="3.9" #Allowed values: 3.7, 3.8, and 3.9 +pythonVersion="3.11" #Allowed values: 3.10, 3.11 share="msdocs-fileshare-$randomIdentifier" directory="msdocs-directory-$randomIdentifier" shareId="msdocs-share-$randomIdentifier" From 5b8106b8be72698976b18fc31de44ca1781e9349 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Fri, 27 Feb 2026 12:26:48 -0800 Subject: [PATCH 08/14] Add Azure Functions sample scripts and update existing scripts for improved functionality and clarity --- azure-functions/README.md | 53 +++++++++++++ .../connect-azure-openai-resources.sh | 13 ++-- .../create-function-app-app-service-plan.sh | 15 ++-- ...reate-function-app-connect-to-cosmos-db.sh | 76 +++++++++++++----- ...function-app-connect-to-storage-account.sh | 70 ++++++++++++++--- .../create-function-app-consumption-python.sh | 32 -------- .../create-function-app-consumption.sh | 12 ++- .../create-function-app-flex-consumption.sh} | 17 ++-- .../create-function-app-premium-plan.sh | 14 ++-- ...ion-app-with-function-github-continuous.sh | 77 ++++++++++++------- ...functions-cli-mount-files-storage-linux.sh | 14 ++-- 11 files changed, 270 insertions(+), 123 deletions(-) create mode 100644 azure-functions/README.md delete mode 100644 azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh rename azure-functions/{create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh => create-function-app-flex-consumption/create-function-app-flex-consumption.sh} (84%) diff --git a/azure-functions/README.md b/azure-functions/README.md new file mode 100644 index 00000000..12975d4c --- /dev/null +++ b/azure-functions/README.md @@ -0,0 +1,53 @@ +--- +page_type: sample +languages: +- azurecli +products: +- azure +- azure-cli +- azure-functions +name: Azure Functions sample scripts +url-fragment: +description: These scripts demonstrate how to create and manage Azure Functions resources using the Azure CLI. +--- +# Azure Functions + +## Azure CLI sample scripts + +These end-to-end Azure CLI scripts help you learn how to provision and manage the Azure resources required by Azure Functions. You must use the [Azure Functions Core Tools][func-core-tools] to create actual Azure Functions code projects from the command line on your local computer and deploy code to these Azure resources. + +For a complete end-to-end example of developing and deploying from the command line using both Core Tools and the Azure CLI, see one of these language-specific [command line quickstarts][func-quickstart]. + +The scripts in this directory demonstrate working with [Azure Functions][func-home] using the [Azure CLI reference commands][azure-cli]. + +| Script | Description | +| ------ | ----------- | +|**Create a function app**|| +|[create-function-app-flex-consumption.sh][af-1]| Creates a function app in a Flex Consumption plan with a user-assigned managed identity. **This is the recommended serverless hosting plan.** | +|[create-function-app-consumption.sh][af-2]| Creates a function app in a Consumption plan. | +|[create-function-app-premium-plan.sh][af-3]| Creates a function app in a Premium (Elastic Premium) plan. | +|[create-function-app-app-service-plan.sh][af-4]| Creates a function app in a dedicated App Service plan. | +|**Connect to services**|| +|[create-function-app-connect-to-storage-account.sh][af-5]| Creates a function app in a Flex Consumption plan and connects it to a storage account using managed identity. | +|[create-function-app-connect-to-cosmos-db.sh][af-6]| Creates a function app in a Flex Consumption plan and connects it to Azure Cosmos DB using managed identity and RBAC. | +|[connect-azure-openai-resources.sh][af-7]| Creates a function app in a Flex Consumption plan and connects it to Azure OpenAI using managed identity. | +|[functions-cli-mount-files-storage-linux.sh][af-8]| Creates a Linux function app and mounts an Azure Files share, which lets you leverage existing data or machine learning models in your functions. | +|**Deploy code**|| +|[deploy-function-app-with-function-github-continuous.sh][af-9]| Creates a function app in a Flex Consumption plan and deploys code from a public GitHub repository. | + + +[af-1]: ./create-function-app-flex-consumption/create-function-app-flex-consumption.sh +[af-2]: ./create-function-app-consumption/create-function-app-consumption.sh +[af-3]: ./create-function-app-premium-plan/create-function-app-premium-plan.sh +[af-4]: ./create-function-app-app-service-plan/create-function-app-app-service-plan.sh +[af-5]: ./create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh +[af-6]: ./create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh +[af-7]: ./connect-azure-openai-resources/connect-azure-openai-resources.sh +[af-8]: ./functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh +[af-9]: ./deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh + + +[func-home]: https://learn.microsoft.com/azure/azure-functions/ +[func-core-tools]: https://learn.microsoft.com/azure/azure-functions/functions-run-local +[func-quickstart]: https://learn.microsoft.com/azure/azure-functions/how-to-create-function-azure-cli +[azure-cli]: https://learn.microsoft.com/cli/azure/reference-index diff --git a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh index c293c227..65dbd841 100644 --- a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh +++ b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh @@ -1,7 +1,6 @@ #!/bin/bash # TODO: Validate in Cloud Shell before merging -# # Function app, storage account, and user identity names must be unique. # Variable block @@ -16,8 +15,8 @@ openaiName="msdocs-openai-$randomIdentifier" modelName="gpt-4o" skuStorage="Standard_LRS" functionsVersion="4" -languageWorker="dotnet-isolated" # Supported values: dotnet-isolated, node, python, powershell, java -languageVersion="8.0" # Supported values: 3.10, 3.11, 7.4, 8.0, 9.0, 10, 11, 17, 20, 21, 22 +languageWorker="python" +languageVersion="3.11" # Install the Application Insights extension az extension add --name application-insights @@ -62,8 +61,10 @@ clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup --query 'clientId' -o tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ - AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" -az functionapp config appsettings delete --name $functionApp --resource-group $resourceGroup --setting-names AzureWebJobsStorage + AzureWebJobsStorage__clientId=$clientId \ + APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp \ + --resource-group $resourceGroup --setting-names AzureWebJobsStorage # Create an Azure OpenAI resource echo "Creating Azure OpenAI resource" @@ -100,7 +101,5 @@ az functionapp config appsettings set --name $functionApp --resource-group $reso AzureOpenAI__clientId=$(echo $user | jq -r '.clientId') \ CHAT_MODEL_DEPLOYMENT_NAME=$modelName -# - # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh b/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh index ae08dbb4..5a3ce822 100644 --- a/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh +++ b/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# +# For the recommended serverless plan, see create-function-app-flex-consumption. # Function app and storage account names must be unique. # Variable block @@ -15,6 +15,8 @@ functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" skuPlan="B1" functionsVersion="4" +runtime="dotnet-isolated" +runtimeVersion="8.0" # Create a resource group echo "Creating $resourceGroup in "$location"..." @@ -26,12 +28,15 @@ az storage account create --name $storage --location "$location" --resource-grou # Create an App Service plan echo "Creating $appServicePlan" -az functionapp plan create --name $appServicePlan --resource-group $resourceGroup --location "$location" --sku $skuPlan +az functionapp plan create --name $appServicePlan --resource-group $resourceGroup \ + --location "$location" --sku $skuPlan # Create a Function App echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $storage --plan $appServicePlan --resource-group $resourceGroup --functions-version $functionsVersion -# +az functionapp create --name $functionApp --storage-account $storage \ + --plan $appServicePlan --resource-group $resourceGroup \ + --runtime $runtime --runtime-version $runtimeVersion \ + --functions-version $functionsVersion # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh index 0140662d..26bc0890 100644 --- a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh +++ b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh @@ -1,9 +1,8 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# -# Function app and storage account names must be unique. +# Function app, storage account, Cosmos DB, and user identity names must be unique. # Variable block let "randomIdentifier=$RANDOM*$RANDOM" @@ -11,36 +10,77 @@ location="eastus" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" tag="create-function-app-connect-to-cosmos-db" storage="msdocsaccount$randomIdentifier" +userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" +cosmosDbAccount="msdocs-cosmosdb-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" +languageWorker="python" +languageVersion="3.11" + +# Install the Application Insights extension +az extension add --name application-insights # Create a resource group echo "Creating $resourceGroup in "$location"..." az group create --name $resourceGroup --location "$location" --tags $tag -# Create a storage account for the function app. +# Create an Azure storage account in the resource group with key access disabled. echo "Creating $storage" -az storage account create --name $storage --location "$location" --resource-group $resourceGroup --sku $skuStorage +az storage account create --name $storage --location "$location" --resource-group $resourceGroup \ + --sku $skuStorage --allow-blob-public-access false --allow-shared-key-access false -# Create a serverless function app in the resource group. -echo "Creating $functionApp" -az functionapp create --name $functionApp --resource-group $resourceGroup --storage-account $storage --consumption-plan-location "$location" --functions-version $functionsVersion +# Create a user-assigned managed identity +echo "Creating $userIdentity" +output=$(az identity create --name $userIdentity --resource-group $resourceGroup --location $location \ + --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) + +# Use jq to parse the output and assign the properties to variables +userId=$(echo $output | jq -r '.userId') +principalId=$(echo $output | jq -r '.principalId') +clientId=$(echo $output | jq -r '.clientId') -# Create an Azure Cosmos DB database account using the same function app name. +# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the identity +storageId=$(az storage account show --resource-group $resourceGroup --name $storage --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $storageId + +# Create the function app in a Flex Consumption plan echo "Creating $functionApp" -az cosmosdb create --name $functionApp --resource-group $resourceGroup +az functionapp create --resource-group $resourceGroup --name $functionApp --flexconsumption-location $location \ + --runtime $languageWorker --runtime-version $languageVersion --storage-account $storage \ + --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value $userIdentity + +# Create a role assignment (Monitoring Metrics Publisher) in Application Insights for the user identity +appInsights=$(az monitor app-insights component show --resource-group $resourceGroup \ + --app $functionApp --query "id" --output tsv) +az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights + +# Update app settings to use managed identities for host storage connections +clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ + --query 'clientId' -o tsv) +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ + AzureWebJobsStorage__clientId=$clientId \ + APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp \ + --resource-group $resourceGroup --setting-names AzureWebJobsStorage -# Get the Azure Cosmos DB connection string. -endpoint=$(az cosmosdb show --name $functionApp --resource-group $resourceGroup --query documentEndpoint --output tsv) -echo $endpoint +# Create an Azure Cosmos DB account +echo "Creating $cosmosDbAccount" +az cosmosdb create --name $cosmosDbAccount --resource-group $resourceGroup --location $location -key=$(az cosmosdb keys list --name $functionApp --resource-group $resourceGroup --query primaryMasterKey --output tsv) -echo $key +# Assign the Cosmos DB Built-in Data Contributor role to the managed identity +cosmosDbId=$(az cosmosdb show --name $cosmosDbAccount --resource-group $resourceGroup --query 'id' -o tsv) +az cosmosdb sql role assignment create --account-name $cosmosDbAccount --resource-group $resourceGroup \ + --role-definition-name "Cosmos DB Built-in Data Contributor" --scope "/" \ + --principal-id $principalId -# Configure function app settings to use the Azure Cosmos DB connection string. -az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup --settings CosmosDB_Endpoint=$endpoint CosmosDB_Key=$key -# +# Get the Cosmos DB endpoint and configure the function app to connect using managed identity +endpoint=$(az cosmosdb show --name $cosmosDbAccount --resource-group $resourceGroup --query documentEndpoint --output tsv) +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings CosmosDB__accountEndpoint=$endpoint CosmosDB__credential=managedidentity \ + CosmosDB__clientId=$clientId # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh b/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh index 3180df13..dab50d82 100644 --- a/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh +++ b/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh @@ -1,8 +1,7 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# -# Function app and storage account names must be unique. +# Function app, storage account, and user identity names must be unique. # Variable block let "randomIdentifier=$RANDOM*$RANDOM" @@ -10,28 +9,75 @@ location="eastus" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" tag="create-function-app-connect-to-storage-account" storage="msdocsaccount$randomIdentifier" +connStorage="msdocsconnaccount$randomIdentifier" +userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" +languageWorker="dotnet-isolated" +languageVersion="8.0" + +# Install the Application Insights extension +az extension add --name application-insights # Create a resource group echo "Creating $resourceGroup in "$location"..." az group create --name $resourceGroup --location "$location" --tags $tag -# Create an Azure storage account in the resource group. +# Create an Azure storage account in the resource group with key access disabled. echo "Creating $storage" -az storage account create --name $storage --location "$location" --resource-group $resourceGroup --sku $skuStorage +az storage account create --name $storage --location "$location" --resource-group $resourceGroup \ + --sku $skuStorage --allow-blob-public-access false --allow-shared-key-access false + +# Create a user-assigned managed identity +echo "Creating $userIdentity" +output=$(az identity create --name $userIdentity --resource-group $resourceGroup --location $location \ + --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) + +# Use jq to parse the output and assign the properties to variables +userId=$(echo $output | jq -r '.userId') +principalId=$(echo $output | jq -r '.principalId') +clientId=$(echo $output | jq -r '.clientId') + +# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the identity +storageId=$(az storage account show --resource-group $resourceGroup --name $storage --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $storageId -# Create a serverless function app in the resource group. +# Create the function app in a Flex Consumption plan echo "Creating $functionApp" -az functionapp create --name $functionApp --resource-group $resourceGroup --storage-account $storage --consumption-plan-location "$location" --functions-version $functionsVersion +az functionapp create --resource-group $resourceGroup --name $functionApp --flexconsumption-location $location \ + --runtime $languageWorker --runtime-version $languageVersion --storage-account $storage \ + --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value $userIdentity + +# Create a role assignment (Monitoring Metrics Publisher) in Application Insights for the user identity +appInsights=$(az monitor app-insights component show --resource-group $resourceGroup \ + --app $functionApp --query "id" --output tsv) +az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights + +# Update app settings to use managed identities for host storage connections +clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ + --query 'clientId' -o tsv) +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ + AzureWebJobsStorage__clientId=$clientId \ + APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp \ + --resource-group $resourceGroup --setting-names AzureWebJobsStorage + +# Create a second storage account for the function app to connect to. +echo "Creating $connStorage" +az storage account create --name $connStorage --location "$location" --resource-group $resourceGroup --sku $skuStorage -# Get the storage account connection string. -connstr=$(az storage account show-connection-string --name $storage --resource-group $resourceGroup --query connectionString --output tsv) +# Assign Storage Blob Data Owner on the second storage account to the managed identity +connStorageId=$(az storage account show --resource-group $resourceGroup --name $connStorage --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $connStorageId -# Update function app settings to connect to the storage account. -az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup --settings StorageConStr=$connstr -# +# Configure the function app to connect to the second storage account using managed identity +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings StorageConnection__serviceUri="https://$connStorage.blob.core.windows.net" \ + StorageConnection__credential=managedidentity StorageConnection__clientId=$clientId # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh b/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh deleted file mode 100644 index 40b9b267..00000000 --- a/azure-functions/create-function-app-consumption-python/create-function-app-consumption-python.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 - -# -# Function app and storage account names must be unique. - -# Variable block -let "randomIdentifier=$RANDOM*$RANDOM" -location="eastus" -resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" -tag="create-function-app-consumption-python" -storage="msdocsaccount$randomIdentifier" -functionApp="msdocs-serverless-python-function-$randomIdentifier" -skuStorage="Standard_LRS" -functionsVersion="4" -pythonVersion="3.11" #Allowed values: 3.10, 3.11 - -# Create a resource group -echo "Creating $resourceGroup in "$location"..." -az group create --name $resourceGroup --location "$location" --tags $tag - -# Create an Azure storage account in the resource group. -echo "Creating $storage" -az storage account create --name $storage --location "$location" --resource-group $resourceGroup --sku $skuStorage - -# Create a serverless python function app in the resource group. -echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $storage --consumption-plan-location "$location" --resource-group $resourceGroup --os-type Linux --runtime python --runtime-version $pythonVersion --functions-version $functionsVersion -# - -# echo "Deleting all resources" -# az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-consumption/create-function-app-consumption.sh b/azure-functions/create-function-app-consumption/create-function-app-consumption.sh index 414d4f15..41f2422e 100644 --- a/azure-functions/create-function-app-consumption/create-function-app-consumption.sh +++ b/azure-functions/create-function-app-consumption/create-function-app-consumption.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# +# For the recommended serverless plan, see create-function-app-flex-consumption. # Function app and storage account names must be unique. # Variable block @@ -13,6 +13,8 @@ storage="msdocsaccount$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" +runtime="dotnet-isolated" +runtimeVersion="8.0" # Create a resource group echo "Creating $resourceGroup in "$location"..." @@ -24,8 +26,10 @@ az storage account create --name $storage --location "$location" --resource-grou # Create a serverless function app in the resource group. echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $storage --consumption-plan-location "$location" --resource-group $resourceGroup --functions-version $functionsVersion -# +az functionapp create --name $functionApp --storage-account $storage \ + --consumption-plan-location "$location" --resource-group $resourceGroup \ + --runtime $runtime --runtime-version $runtimeVersion \ + --functions-version $functionsVersion # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh b/azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh similarity index 84% rename from azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh rename to azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh index eeb03d9a..f1198cbc 100644 --- a/azure-functions/create-function-app-flex-plan-identities/create-function-app-flex-plan-identities.sh +++ b/azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh @@ -1,21 +1,21 @@ #!/bin/bash -# Passed validation in Cloud Shell on 5/15/2025 +# TODO: Validate in Cloud Shell before merging -# +# Flex Consumption is the recommended plan for most serverless workloads. # Function app, storage account, and user identity names must be unique. # Variable block let "randomIdentifier=$RANDOM*$RANDOM" location="northeurope" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" -tag="create-function-app-flex-plan-identities" +tag="create-function-app-flex-consumption" storage="msdocsaccount$randomIdentifier" userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" -languageWorker="dotnet-isolated" # Supported values: dotnet-isolated, node, python, powershell, java -languageVersion="8.0" # Supported values: 3.10, 3.11, 7.4, 8.0, 9.0, 10, 11, 17, 20, 21, 22 +languageWorker="python" +languageVersion="3.11" # Install the Application Insights extension az extension add --name application-insights @@ -60,9 +60,10 @@ clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup --query 'clientId' -o tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ - AzureWebJobsStorage__clientId=$clientId APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" -az functionapp config appsettings delete --name $functionApp --resource-group $resourceGroup --setting-names AzureWebJobsStorage -# + AzureWebJobsStorage__clientId=$clientId \ + APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp \ + --resource-group $resourceGroup --setting-names AzureWebJobsStorage # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh b/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh index cb62bbe6..db1208b7 100644 --- a/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh +++ b/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh @@ -1,7 +1,7 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# +# For the recommended serverless plan, see create-function-app-flex-consumption. # Function app and storage account names must be unique. # Variable block @@ -12,9 +12,11 @@ tag="create-function-app-premium-plan" storage="msdocsaccount$randomIdentifier" premiumPlan="msdocs-premium-plan-$randomIdentifier" functionApp="msdocs-function-$randomIdentifier" -skuStorage="Standard_LRS" # Allowed values: Standard_LRS, Standard_GRS, Standard_RAGRS, Standard_ZRS, Premium_LRS, Premium_ZRS, Standard_GZRS, Standard_RAGZRS +skuStorage="Standard_LRS" skuPlan="EP1" functionsVersion="4" +runtime="node" +runtimeVersion="20" # Create a resource group echo "Creating $resourceGroup in "$location"..." @@ -30,8 +32,10 @@ az functionapp plan create --name $premiumPlan --resource-group $resourceGroup - # Create a Function App echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $storage --plan $premiumPlan --resource-group $resourceGroup --functions-version $functionsVersion -# +az functionapp create --name $functionApp --storage-account $storage \ + --plan $premiumPlan --resource-group $resourceGroup --os-type Linux \ + --runtime $runtime --runtime-version $runtimeVersion \ + --functions-version $functionsVersion # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh index 090ad706..f868f9fb 100644 --- a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh +++ b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh @@ -1,44 +1,69 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# - -# Function app and storage account names must be unique. +# Function app, storage account, and user identity names must be unique. let "randomIdentifier=$RANDOM*$RANDOM" -location=eastus +location="eastus" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" tag="deploy-function-app-with-function-github" -storage="msdocs$randomIdentifier" +storage="msdocsaccount$randomIdentifier" +userIdentity="msdocs-managed-identity-$randomIdentifier" +functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" -functionApp=mygithubfunc$randomIdentifier functionsVersion="4" -runtime="node" -runtimeVersion="20" +languageWorker="node" +languageVersion="20" # Public GitHub repository containing an Azure Functions code project. gitrepo=https://github.com/Azure-Samples/functions-quickstart-javascript -## Enable authenticated git deployment in your subscription when using a private repo. -#token= -#az functionapp deployment source update-token \ -# --git-token $token -# Create a resource group. -echo "Creating $resourceGroup in ""$location""..." +# Install the Application Insights extension +az extension add --name application-insights + +# Create a resource group +echo "Creating $resourceGroup in "$location"..." az group create --name $resourceGroup --location "$location" --tags $tag -# Create an Azure storage account in the resource group. +# Create an Azure storage account in the resource group with key access disabled. echo "Creating $storage" -az storage account create --name $storage --location "$location" --resource-group $resourceGroup --sku $skuStorage +az storage account create --name $storage --location "$location" --resource-group $resourceGroup \ + --sku $skuStorage --allow-blob-public-access false --allow-shared-key-access false + +# Create a user-assigned managed identity +echo "Creating $userIdentity" +output=$(az identity create --name $userIdentity --resource-group $resourceGroup --location $location \ + --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) + +# Use jq to parse the output and assign the properties to variables +userId=$(echo $output | jq -r '.userId') +principalId=$(echo $output | jq -r '.principalId') +clientId=$(echo $output | jq -r '.clientId') -# Create a function app with source files deployed from the specified GitHub repo. +# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the identity +storageId=$(az storage account show --resource-group $resourceGroup --name $storage --query 'id' -o tsv) +az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ + --role "Storage Blob Data Owner" --scope $storageId + +# Create the function app in a Flex Consumption plan with source files deployed from GitHub echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $storage \ - --consumption-plan-location "$location" --resource-group $resourceGroup \ - --deployment-source-url $gitrepo --deployment-source-branch main \ - --functions-version $functionsVersion --runtime $runtime --runtime-version $runtimeVersion - -# Connect to function application -curl -s "https://${functionApp}.azurewebsites.net/api/httpexample?name=Azure" -# +az functionapp create --resource-group $resourceGroup --name $functionApp --flexconsumption-location $location \ + --runtime $languageWorker --runtime-version $languageVersion --storage-account $storage \ + --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value $userIdentity \ + --deployment-source-url $gitrepo --deployment-source-branch main + +# Create a role assignment (Monitoring Metrics Publisher) in Application Insights for the user identity +appInsights=$(az monitor app-insights component show --resource-group $resourceGroup \ + --app $functionApp --query "id" --output tsv) +az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights + +# Update app settings to use managed identities for host storage connections +clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ + --query 'clientId' -o tsv) +az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ + --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ + AzureWebJobsStorage__clientId=$clientId \ + APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" +az functionapp config appsettings delete --name $functionApp \ + --resource-group $resourceGroup --setting-names AzureWebJobsStorage # echo "Deleting all resources" # az group delete --name $resourceGroup -y diff --git a/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh b/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh index a5fecf77..fea99760 100644 --- a/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh +++ b/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh @@ -1,7 +1,6 @@ #!/bin/bash -# Passed validation in Cloud Shell on 3/24/2022 +# TODO: Validate in Cloud Shell before merging -# # Function app and storage account names must be unique. # Variable block @@ -13,7 +12,7 @@ export AZURE_STORAGE_ACCOUNT="msdocsstorage$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" -pythonVersion="3.11" #Allowed values: 3.10, 3.11 +pythonVersion="3.11" share="msdocs-fileshare-$randomIdentifier" directory="msdocs-directory-$randomIdentifier" shareId="msdocs-share-$randomIdentifier" @@ -25,14 +24,18 @@ az group create --name $resourceGroup --location "$location" --tags $tag # Create an Azure storage account in the resource group. echo "Creating $AZURE_STORAGE_ACCOUNT" -az storage account create --name $AZURE_STORAGE_ACCOUNT --location "$location" --resource-group $resourceGroup --sku $skuStorage +az storage account create --name $AZURE_STORAGE_ACCOUNT --location "$location" \ + --resource-group $resourceGroup --sku $skuStorage # Set the storage account key as an environment variable. export AZURE_STORAGE_KEY=$(az storage account keys list -g $resourceGroup -n $AZURE_STORAGE_ACCOUNT --query '[0].value' -o tsv) # Create a serverless function app in the resource group. echo "Creating $functionApp" -az functionapp create --name $functionApp --storage-account $AZURE_STORAGE_ACCOUNT --consumption-plan-location "$location" --resource-group $resourceGroup --os-type Linux --runtime python --runtime-version $pythonVersion --functions-version $functionsVersion +az functionapp create --name $functionApp --storage-account $AZURE_STORAGE_ACCOUNT \ + --consumption-plan-location "$location" --resource-group $resourceGroup \ + --os-type Linux --runtime python --runtime-version $pythonVersion \ + --functions-version $functionsVersion # Work with Storage account using the set env variables. # Create a share in Azure Files. @@ -57,7 +60,6 @@ az webapp config storage-account add \ # List webapp storage account az webapp config storage-account list --resource-group $resourceGroup --name $functionApp -# # echo "Deleting all resources" # az group delete --name $resourceGroup -y From 33a18d8ba69ea103d82aca39c80200d26768a779 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Sat, 28 Feb 2026 11:04:13 -0800 Subject: [PATCH 09/14] Change GH deployment script back to Consumption plan and remove unused identity management code --- ...ion-app-with-function-github-continuous.sh | 54 ++++--------------- 1 file changed, 10 insertions(+), 44 deletions(-) diff --git a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh index f868f9fb..128011a5 100644 --- a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh +++ b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh @@ -1,69 +1,35 @@ #!/bin/bash # TODO: Validate in Cloud Shell before merging -# Function app, storage account, and user identity names must be unique. +# Function app and storage account names must be unique. let "randomIdentifier=$RANDOM*$RANDOM" location="eastus" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" tag="deploy-function-app-with-function-github" storage="msdocsaccount$randomIdentifier" -userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" skuStorage="Standard_LRS" functionsVersion="4" -languageWorker="node" -languageVersion="20" +runtime="node" +runtimeVersion="20" # Public GitHub repository containing an Azure Functions code project. gitrepo=https://github.com/Azure-Samples/functions-quickstart-javascript -# Install the Application Insights extension -az extension add --name application-insights - # Create a resource group echo "Creating $resourceGroup in "$location"..." az group create --name $resourceGroup --location "$location" --tags $tag -# Create an Azure storage account in the resource group with key access disabled. +# Create an Azure storage account in the resource group. echo "Creating $storage" -az storage account create --name $storage --location "$location" --resource-group $resourceGroup \ - --sku $skuStorage --allow-blob-public-access false --allow-shared-key-access false - -# Create a user-assigned managed identity -echo "Creating $userIdentity" -output=$(az identity create --name $userIdentity --resource-group $resourceGroup --location $location \ - --query "{userId:id, principalId: principalId, clientId: clientId}" -o json) - -# Use jq to parse the output and assign the properties to variables -userId=$(echo $output | jq -r '.userId') -principalId=$(echo $output | jq -r '.principalId') -clientId=$(echo $output | jq -r '.clientId') +az storage account create --name $storage --location "$location" --resource-group $resourceGroup --sku $skuStorage -# Get the storage ID and create a role assignment (Storage Blob Data Owner) for the identity -storageId=$(az storage account show --resource-group $resourceGroup --name $storage --query 'id' -o tsv) -az role assignment create --assignee-object-id $principalId --assignee-principal-type ServicePrincipal \ - --role "Storage Blob Data Owner" --scope $storageId - -# Create the function app in a Flex Consumption plan with source files deployed from GitHub +# Create a serverless function app in the resource group. echo "Creating $functionApp" -az functionapp create --resource-group $resourceGroup --name $functionApp --flexconsumption-location $location \ - --runtime $languageWorker --runtime-version $languageVersion --storage-account $storage \ - --deployment-storage-auth-type UserAssignedIdentity --deployment-storage-auth-value $userIdentity \ +az functionapp create --name $functionApp --storage-account $storage \ + --consumption-plan-location "$location" --resource-group $resourceGroup \ + --runtime $runtime --runtime-version $runtimeVersion \ + --functions-version $functionsVersion \ --deployment-source-url $gitrepo --deployment-source-branch main -# Create a role assignment (Monitoring Metrics Publisher) in Application Insights for the user identity -appInsights=$(az monitor app-insights component show --resource-group $resourceGroup \ - --app $functionApp --query "id" --output tsv) -az role assignment create --role "Monitoring Metrics Publisher" --assignee $principalId --scope $appInsights - -# Update app settings to use managed identities for host storage connections -clientId=$(az identity show --name $userIdentity --resource-group $resourceGroup \ - --query 'clientId' -o tsv) -az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ - --settings AzureWebJobsStorage__accountName=$storage AzureWebJobsStorage__credential=managedidentity \ - AzureWebJobsStorage__clientId=$clientId \ - APPLICATIONINSIGHTS_AUTHENTICATION_STRING="ClientId=$clientId;Authorization=AAD" -az functionapp config appsettings delete --name $functionApp \ - --resource-group $resourceGroup --setting-names AzureWebJobsStorage - # echo "Deleting all resources" # az group delete --name $resourceGroup -y From 4eed0b4f4db646c602ae34e79323044f18efc4d8 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Sat, 28 Feb 2026 11:38:29 -0800 Subject: [PATCH 10/14] Update Cosmos DB creation command to include location and failover settings --- .../create-function-app-connect-to-cosmos-db.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh index 26bc0890..52d7a0fd 100644 --- a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh +++ b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh @@ -68,7 +68,8 @@ az functionapp config appsettings delete --name $functionApp \ # Create an Azure Cosmos DB account echo "Creating $cosmosDbAccount" -az cosmosdb create --name $cosmosDbAccount --resource-group $resourceGroup --location $location +az cosmosdb create --name $cosmosDbAccount --resource-group $resourceGroup \ + --locations regionName=$location failoverPriority=0 isZoneRedundant=False # Assign the Cosmos DB Built-in Data Contributor role to the managed identity cosmosDbId=$(az cosmosdb show --name $cosmosDbAccount --resource-group $resourceGroup --query 'id' -o tsv) From d319f65efe8d2e4dde3db851c64dee8f3b1c7a28 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Sat, 28 Feb 2026 11:43:32 -0800 Subject: [PATCH 11/14] Update location in Azure OpenAI resources script from northeurope to swedencentral --- .../connect-azure-openai-resources.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh index 65dbd841..5171c994 100644 --- a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh +++ b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh @@ -5,7 +5,7 @@ # Variable block let "randomIdentifier=$RANDOM*$RANDOM" -location="northeurope" +location="swedencentral" resourceGroup="msdocs-azure-functions-rg-$randomIdentifier" tag="connect-azure-openai-resources" storage="msdocsaccount$randomIdentifier" From 36088576c7701d7d6f9387aa7034ea924887cbcb Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Sat, 28 Feb 2026 12:16:25 -0800 Subject: [PATCH 12/14] Update validation comments in Azure Functions scripts to reflect successful Cloud Shell validation --- .../connect-azure-openai-resources.sh | 2 +- .../create-function-app-app-service-plan.sh | 2 +- .../create-function-app-connect-to-cosmos-db.sh | 2 +- .../create-function-app-connect-to-storage-account.sh | 2 +- .../create-function-app-consumption.sh | 2 +- .../create-function-app-flex-consumption.sh | 2 +- .../create-function-app-premium-plan.sh | 2 +- .../deploy-function-app-with-function-github-continuous.sh | 2 +- .../functions-cli-mount-files-storage-linux.sh | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh index 5171c994..07391865 100644 --- a/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh +++ b/azure-functions/connect-azure-openai-resources/connect-azure-openai-resources.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # Function app, storage account, and user identity names must be unique. diff --git a/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh b/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh index 5a3ce822..e5665aa5 100644 --- a/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh +++ b/azure-functions/create-function-app-app-service-plan/create-function-app-app-service-plan.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # For the recommended serverless plan, see create-function-app-flex-consumption. # Function app and storage account names must be unique. diff --git a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh index 52d7a0fd..9a6e2f4f 100644 --- a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh +++ b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh @@ -1,6 +1,6 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # Function app, storage account, Cosmos DB, and user identity names must be unique. diff --git a/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh b/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh index dab50d82..36d05762 100644 --- a/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh +++ b/azure-functions/create-function-app-connect-to-storage/create-function-app-connect-to-storage-account.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # Function app, storage account, and user identity names must be unique. diff --git a/azure-functions/create-function-app-consumption/create-function-app-consumption.sh b/azure-functions/create-function-app-consumption/create-function-app-consumption.sh index 41f2422e..ba45e6b2 100644 --- a/azure-functions/create-function-app-consumption/create-function-app-consumption.sh +++ b/azure-functions/create-function-app-consumption/create-function-app-consumption.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # For the recommended serverless plan, see create-function-app-flex-consumption. # Function app and storage account names must be unique. diff --git a/azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh b/azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh index f1198cbc..091daf3d 100644 --- a/azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh +++ b/azure-functions/create-function-app-flex-consumption/create-function-app-flex-consumption.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # Flex Consumption is the recommended plan for most serverless workloads. # Function app, storage account, and user identity names must be unique. diff --git a/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh b/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh index db1208b7..2b841180 100644 --- a/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh +++ b/azure-functions/create-function-app-premium-plan/create-function-app-premium-plan.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # For the recommended serverless plan, see create-function-app-flex-consumption. # Function app and storage account names must be unique. diff --git a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh index 128011a5..2a79bf6f 100644 --- a/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh +++ b/azure-functions/deploy-function-app-with-function-github-continuous/deploy-function-app-with-function-github-continuous.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # Function app and storage account names must be unique. let "randomIdentifier=$RANDOM*$RANDOM" diff --git a/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh b/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh index fea99760..520115ec 100644 --- a/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh +++ b/azure-functions/functions-cli-mount-files-storage-linux/functions-cli-mount-files-storage-linux.sh @@ -1,5 +1,5 @@ #!/bin/bash -# TODO: Validate in Cloud Shell before merging +# Passed validation in Cloud Shell on 2/28/2026 # Function app and storage account names must be unique. From 40f47ed75b73c2527582bab185b8a9e2ece07240 Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Sat, 28 Feb 2026 12:28:28 -0800 Subject: [PATCH 13/14] Update README.md to include links for Azure Function plans in script descriptions --- azure-functions/README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/azure-functions/README.md b/azure-functions/README.md index 12975d4c..629377f2 100644 --- a/azure-functions/README.md +++ b/azure-functions/README.md @@ -23,17 +23,17 @@ The scripts in this directory demonstrate working with [Azure Functions][func-ho | Script | Description | | ------ | ----------- | |**Create a function app**|| -|[create-function-app-flex-consumption.sh][af-1]| Creates a function app in a Flex Consumption plan with a user-assigned managed identity. **This is the recommended serverless hosting plan.** | -|[create-function-app-consumption.sh][af-2]| Creates a function app in a Consumption plan. | -|[create-function-app-premium-plan.sh][af-3]| Creates a function app in a Premium (Elastic Premium) plan. | -|[create-function-app-app-service-plan.sh][af-4]| Creates a function app in a dedicated App Service plan. | +|[create-function-app-flex-consumption.sh][af-1]| Creates a function app in a [Flex Consumption plan][plan-flex] with a user-assigned managed identity. **This is the recommended serverless hosting plan.** | +|[create-function-app-consumption.sh][af-2]| Creates a function app in a [Consumption plan][plan-consumption]. | +|[create-function-app-premium-plan.sh][af-3]| Creates a function app in a [Premium (Elastic Premium) plan][plan-premium]. | +|[create-function-app-app-service-plan.sh][af-4]| Creates a function app in a dedicated [App Service plan][plan-dedicated]. | |**Connect to services**|| -|[create-function-app-connect-to-storage-account.sh][af-5]| Creates a function app in a Flex Consumption plan and connects it to a storage account using managed identity. | -|[create-function-app-connect-to-cosmos-db.sh][af-6]| Creates a function app in a Flex Consumption plan and connects it to Azure Cosmos DB using managed identity and RBAC. | -|[connect-azure-openai-resources.sh][af-7]| Creates a function app in a Flex Consumption plan and connects it to Azure OpenAI using managed identity. | +|[create-function-app-connect-to-storage-account.sh][af-5]| Creates a function app in a [Flex Consumption plan][plan-flex] and connects it to a storage account using managed identity. | +|[create-function-app-connect-to-cosmos-db.sh][af-6]| Creates a function app in a [Flex Consumption plan][plan-flex] and connects it to Azure Cosmos DB using managed identity and RBAC. | +|[connect-azure-openai-resources.sh][af-7]| Creates a function app in a [Flex Consumption plan][plan-flex] and connects it to Azure OpenAI using managed identity. | |[functions-cli-mount-files-storage-linux.sh][af-8]| Creates a Linux function app and mounts an Azure Files share, which lets you leverage existing data or machine learning models in your functions. | |**Deploy code**|| -|[deploy-function-app-with-function-github-continuous.sh][af-9]| Creates a function app in a Flex Consumption plan and deploys code from a public GitHub repository. | +|[deploy-function-app-with-function-github-continuous.sh][af-9]| Creates a function app in a [Consumption plan][plan-consumption] and deploys code from a public GitHub repository. | [af-1]: ./create-function-app-flex-consumption/create-function-app-flex-consumption.sh @@ -51,3 +51,7 @@ The scripts in this directory demonstrate working with [Azure Functions][func-ho [func-core-tools]: https://learn.microsoft.com/azure/azure-functions/functions-run-local [func-quickstart]: https://learn.microsoft.com/azure/azure-functions/how-to-create-function-azure-cli [azure-cli]: https://learn.microsoft.com/cli/azure/reference-index +[plan-flex]: https://learn.microsoft.com/azure/azure-functions/flex-consumption-plan +[plan-consumption]: https://learn.microsoft.com/azure/azure-functions/consumption-plan +[plan-premium]: https://learn.microsoft.com/azure/azure-functions/functions-premium-plan +[plan-dedicated]: https://learn.microsoft.com/azure/azure-functions/dedicated-plan From 4df344416db257c28888df468b662d01c74c136f Mon Sep 17 00:00:00 2001 From: Glenn Gailey Date: Mon, 2 Mar 2026 12:25:54 -0800 Subject: [PATCH 14/14] Add Cosmos DB database and container creation to function app script --- .../create-function-app-connect-to-cosmos-db.sh | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh index 9a6e2f4f..e36d42ed 100644 --- a/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh +++ b/azure-functions/create-function-app-connect-to-cosmos-db/create-function-app-connect-to-cosmos-db.sh @@ -13,6 +13,8 @@ storage="msdocsaccount$randomIdentifier" userIdentity="msdocs-managed-identity-$randomIdentifier" functionApp="msdocs-serverless-function-$randomIdentifier" cosmosDbAccount="msdocs-cosmosdb-$randomIdentifier" +cosmosDbDatabase="documents-db" +cosmosDbContainer="documents" skuStorage="Standard_LRS" functionsVersion="4" languageWorker="python" @@ -71,6 +73,14 @@ echo "Creating $cosmosDbAccount" az cosmosdb create --name $cosmosDbAccount --resource-group $resourceGroup \ --locations regionName=$location failoverPriority=0 isZoneRedundant=False +# Create a database and containers for the Cosmos DB account +az cosmosdb sql database create --account-name $cosmosDbAccount --resource-group $resourceGroup \ + --name $cosmosDbDatabase +az cosmosdb sql container create --account-name $cosmosDbAccount --resource-group $resourceGroup \ + --database-name $cosmosDbDatabase --name $cosmosDbContainer --partition-key-path "/id" +az cosmosdb sql container create --account-name $cosmosDbAccount --resource-group $resourceGroup \ + --database-name $cosmosDbDatabase --name leases --partition-key-path "/id" + # Assign the Cosmos DB Built-in Data Contributor role to the managed identity cosmosDbId=$(az cosmosdb show --name $cosmosDbAccount --resource-group $resourceGroup --query 'id' -o tsv) az cosmosdb sql role assignment create --account-name $cosmosDbAccount --resource-group $resourceGroup \ @@ -80,8 +90,9 @@ az cosmosdb sql role assignment create --account-name $cosmosDbAccount --resourc # Get the Cosmos DB endpoint and configure the function app to connect using managed identity endpoint=$(az cosmosdb show --name $cosmosDbAccount --resource-group $resourceGroup --query documentEndpoint --output tsv) az functionapp config appsettings set --name $functionApp --resource-group $resourceGroup \ - --settings CosmosDB__accountEndpoint=$endpoint CosmosDB__credential=managedidentity \ - CosmosDB__clientId=$clientId + --settings COSMOS_CONNECTION__accountEndpoint=$endpoint COSMOS_CONNECTION__credential=managedidentity \ + COSMOS_CONNECTION__clientId=$clientId \ + COSMOS_DATABASE_NAME=$cosmosDbDatabase COSMOS_CONTAINER_NAME=$cosmosDbContainer # echo "Deleting all resources" # az group delete --name $resourceGroup -y