mirror of
				https://github.com/Azure-Samples/graphrag-accelerator.git
				synced 2025-10-30 18:18:13 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			269 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Bash
		
	
	
		
			Executable File
		
	
	
	
	
| #!/bin/bash
 | |
| 
 | |
| set -eu # use set -eux for debugging
 | |
| 
 | |
| function load_env_variables() {
 | |
|     set -a
 | |
|     source .env
 | |
|     set +a
 | |
| }
 | |
| 
 | |
| function checkRequiredParams () {
 | |
|     requiredParams=(
 | |
|     LOCATION
 | |
|     RESOURCE_GROUP
 | |
|     SUBSCRIPTION_ID
 | |
|     AAD_CLIENT_ID
 | |
|     AAD_OBJECT_ID
 | |
|     AAD_TENANT_ID
 | |
|     )
 | |
|     local paramsFile=$1
 | |
|     for param in "${requiredParams[@]}"; do
 | |
|         local paramValue=$(jq -r .$param < $paramsFile)
 | |
|         if [ "null" == "$paramValue" ] || [ -z "$paramValue" ]; then
 | |
|             echo "Parameter $param is required, exiting..."
 | |
|             exit 1
 | |
|         fi
 | |
|     done
 | |
| }
 | |
| 
 | |
| function populateRequiredParams () {
 | |
|     local paramsFile=$1
 | |
|     printf "Checking required parameters... "
 | |
|     checkRequiredParams $paramsFile
 | |
|     # The jq command below sets environment variables based on the key-value pairs in a JSON-formatted file
 | |
|     eval $(jq -r 'to_entries | .[] | "export \(.key)=\(.value)"' $paramsFile)
 | |
|     printf "Done.\n"
 | |
| }
 | |
| 
 | |
| function set_variables() {
 | |
|     printf "Setting environment variables...\n"
 | |
|     SUBSCRIPTION_ID=${SUBSCRIPTION_ID:-""}
 | |
|     RESOURCE_GROUP=${RESOURCE_GROUP:-""}
 | |
|     LOCATION=${LOCATION:-""}
 | |
|     AAD_CLIENT_ID=${AAD_CLIENT_ID:-""}
 | |
|     AAD_OBJECT_ID=${AAD_OBJECT_ID:-""}
 | |
|     AAD_TENANT_ID=${AAD_TENANT_ID:-""}
 | |
|     AAD_TOKEN_ISSUER_URL=${AAD_TOKEN_ISSUER_URL:-"https://login.microsoftonline.com/$AAD_TENANT_ID/v2.0"}
 | |
|     IMAGE_NAME=${IMAGE_NAME:-"graphrag:frontend"}
 | |
|     REGISTRY_NAME=${REGISTRY_NAME:-"${RESOURCE_GROUP}reg"}
 | |
|     APP_SERVICE_PLAN=${APP_SERVICE_PLAN:-"${RESOURCE_GROUP}-asp"}
 | |
|     WEB_APP=${WEB_APP:-"${RESOURCE_GROUP}-playground"}
 | |
|     WEB_APP_IDENTITY=${WEB_APP_IDENTITY:-"${WEB_APP}-identity"}
 | |
|     #BACKEND_RESOURCE_GROUP=${BACKEND_RESOURCE_GROUP:-""} # needed for backend outbound vnet integration
 | |
|     printf "Done setting environment variables.\n"
 | |
| }
 | |
| 
 | |
| function create_resource_group {
 | |
|     printf "Setting subsctiption to $SUBSCRIPTION_ID and Creating resource group...\n"
 | |
|     az account set --subscription $SUBSCRIPTION_ID > /dev/null
 | |
|     az group create --name $RESOURCE_GROUP --location $LOCATION > /dev/null
 | |
|     printf "Resource group created.\n"
 | |
| }
 | |
| 
 | |
| function create_acr() {
 | |
|     printf "Creating Azure Container Registry...\n"
 | |
|     az acr create --resource-group $RESOURCE_GROUP \
 | |
|     --name $REGISTRY_NAME \
 | |
|     --sku Basic \
 | |
|     --admin-enabled false > /dev/null
 | |
|     printf "Azure Container Registry created.\n"
 | |
| }
 | |
| 
 | |
| function build_and_push_image() {
 | |
|     printf "Building and pushing image...\n"
 | |
|     local SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]:-$0}"; )" &> /dev/null && pwd 2> /dev/null; )";
 | |
|     az acr build --registry $REGISTRY_NAME -f $SCRIPT_DIR/../docker/Dockerfile-frontend --image $IMAGE_NAME $SCRIPT_DIR/../
 | |
|     printf "Image built and pushed.\n"
 | |
| }
 | |
| 
 | |
| function create_app_service_plan() {
 | |
|     printf "Creating app service plan...\n"
 | |
|     az appservice plan create --name $APP_SERVICE_PLAN \
 | |
|         --resource-group $RESOURCE_GROUP \
 | |
|         --sku B3 \
 | |
|         --is-linux > /dev/null
 | |
|     printf "App service plan created.\n"
 | |
| }
 | |
| 
 | |
| 
 | |
| function create_web_app_identity() {
 | |
|     printf "Creating web app identity...\n"
 | |
|     IDENTITY_RESULT=$(az identity create --resource-group $RESOURCE_GROUP --name $WEB_APP_IDENTITY --output json)
 | |
|     WEBAPP_IDENTITY_ID=$(jq -r .id <<< $IDENTITY_RESULT)
 | |
|     WEBAPP_IDENTITY_OBJECT_ID=$(jq -r .principalId <<< $IDENTITY_RESULT)
 | |
|     WEBAPP_IDENTITY_CLIENT_ID=$(jq -r .clientId <<< $IDENTITY_RESULT)
 | |
|     printf "Web app identity created.\n"
 | |
| }
 | |
| 
 | |
| function configure_registry_credentials() {
 | |
|     printf "Configuring registry credentials...\n"
 | |
|     ACR_ID=$(az acr show --name $REGISTRY_NAME --resource-group $RESOURCE_GROUP --query id --output tsv)
 | |
|     az role assignment create --assignee $WEBAPP_IDENTITY_CLIENT_ID \
 | |
|         --role AcrPull \
 | |
|         --scope $ACR_ID > /dev/null
 | |
|     printf "Registry credentials configured.\n"
 | |
| }
 | |
| 
 | |
| function create_web_app() {
 | |
|     printf "Creating web app...\n"
 | |
|     az webapp create --resource-group $RESOURCE_GROUP \
 | |
|         --plan $APP_SERVICE_PLAN \
 | |
|         --name $WEB_APP \
 | |
|         --assign-identity $WEBAPP_IDENTITY_ID \
 | |
|         --acr-use-identity \
 | |
|         --acr-identity $WEBAPP_IDENTITY_ID \
 | |
|         --https-only true \
 | |
|         --container-image-name $REGISTRY_NAME.azurecr.io/$IMAGE_NAME > /dev/null
 | |
|     printf "Web app created.\n"
 | |
| }
 | |
| 
 | |
| function configure_app_settings() {
 | |
|     printf "Configuring app settings...\n"
 | |
|     APP_SETTINGS=""
 | |
|     while IFS='=' read -r name value
 | |
|     do
 | |
|         value="${value%\"}"   # Remove opening quote
 | |
|         value="${value#\"}"   # Remove closing quote
 | |
|         APP_SETTINGS="$APP_SETTINGS $name=$value"
 | |
|     done < .env
 | |
|     # echo $APP_SETTINGS
 | |
|     az webapp config appsettings set --name $WEB_APP \
 | |
|         --resource-group $RESOURCE_GROUP \
 | |
|         --settings $APP_SETTINGS > /dev/null
 | |
|     printf "App settings configured.\n"
 | |
| }
 | |
| 
 | |
| function create_federated_identity_credentials() {
 | |
|     printf "Creating federated identity credentials...\n"
 | |
|     EXISTING_CREDENTIAL_SUBJECTS=$(az rest --method GET --uri "https://graph.microsoft.com/beta/applications/$AAD_OBJECT_ID/federatedIdentityCredentials" -o json | jq -r '.value[].subject')
 | |
|     if [[ "$EXISTING_CREDENTIAL_SUBJECTS" == *"$WEBAPP_IDENTITY_OBJECT_ID"* ]]; then
 | |
|         echo "Federated identity credential already exists for the subject: $WEBAPP_IDENTITY_OBJECT_ID"
 | |
|     else
 | |
|         az webapp auth update \
 | |
|             --name $WEB_APP \
 | |
|             --resource-group $RESOURCE_GROUP \
 | |
|             --enabled true \
 | |
|             --action LoginWithAzureActiveDirectory \
 | |
|             --aad-client-id $AAD_CLIENT_ID \
 | |
|             --aad-token-issuer-url $AAD_TOKEN_ISSUER_URL > /dev/null
 | |
|         az rest --method POST \
 | |
|             --uri "https://graph.microsoft.com/beta/applications/$AAD_OBJECT_ID/federatedIdentityCredentials" \
 | |
|             --body "{'name': '$WEB_APP', 'issuer': '$AAD_TOKEN_ISSUER_URL', 'subject': '$WEBAPP_IDENTITY_OBJECT_ID', 'audiences': [ 'api://AzureADTokenExchange' ]}" > /dev/null
 | |
|     fi
 | |
|     printf "Federated identity credentials created.\n"
 | |
| }
 | |
| 
 | |
| function configure_auth_settings() {
 | |
|     printf "Configuring auth settings...\n"
 | |
|     az webapp config appsettings set --resource-group $RESOURCE_GROUP \
 | |
|         --name $WEB_APP \
 | |
|         --slot-settings OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID=$WEBAPP_IDENTITY_CLIENT_ID \
 | |
|         --verbose > /dev/null
 | |
|     az webapp config appsettings list --resource-group $RESOURCE_GROUP \
 | |
|         --name $WEB_APP > /dev/null
 | |
| 
 | |
|     authSettings=$(az rest --method GET --url "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Web/sites/$WEB_APP/config/authsettingsV2/list?api-version=2020-12-01" --output json)
 | |
|     echo $authSettings > auth.json
 | |
|     jq '.properties.identityProviders.azureActiveDirectory.registration.clientSecretSettingName = "OVERRIDE_USE_MI_FIC_ASSERTION_CLIENTID"' auth.json > tmp.json && mv tmp.json auth.json # pragma: allowlist secret
 | |
|     az rest --method PUT \
 | |
|         --url "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Web/sites/$WEB_APP/config/authsettingsV2?api-version=2020-12-01" \
 | |
|         --body @auth.json \
 | |
|         --headers "Content-Type=application/json" > /dev/null
 | |
|     rm auth.json
 | |
|     printf "Auth settings configured.\n"
 | |
| }
 | |
| 
 | |
| function update_appreg_redirect_uris() {
 | |
|     printf "Updating app registration redirect URIs...\n"
 | |
|     WEB_APP_URL=$(az webapp show --name $WEB_APP --resource-group $RESOURCE_GROUP --query defaultHostName --output tsv)
 | |
|     NEW_REDIRECT_URI=https://$WEB_APP_URL/.auth/login/aad/callback
 | |
|     # Fetch the current list of web redirect URIs
 | |
|     CURRENT_URIS=$(az ad app show --id $AAD_CLIENT_ID --query "web.redirectUris" --output tsv)
 | |
|     if ! echo "${CURRENT_URIS}" | grep -q "${NEW_REDIRECT_URI}"; then
 | |
|         az ad app update --id $AAD_CLIENT_ID --web-redirect-uris ${CURRENT_URIS[@]} "$NEW_REDIRECT_URI" > /dev/null
 | |
|     fi
 | |
|     printf "App registration redirect URIs updated.\n"
 | |
| }
 | |
| 
 | |
| function restart_web_app() {
 | |
|     printf "Restarting web app...\n"
 | |
|     az webapp restart --name $WEB_APP --resource-group $RESOURCE_GROUP > /dev/null
 | |
|     printf "Waiting for webapp to restart, webapp might take a few minutes to load.....\n"
 | |
|     sleep 180
 | |
|     printf "Web app restarted. \n"
 | |
| }
 | |
| 
 | |
| ## The following function adds outbound vnet integration on the webapp so that the frontend container can access resources in the AKS cluster directly.
 | |
| ## This will create a new subnet named "frontend" in the backend resource group's vnet if it does not exist.
 | |
| ## This may not be needed in simplified backend architecture but useful for folks using a prior version of the accelerator that had a different network architecture.
 | |
| # function add_vnet_integration() {
 | |
| #     VNET_NAME=$(az network vnet list --resource-group $BACKEND_RESOURCE_GROUP --query "[0].name" --output tsv)
 | |
| #     VNET_ID=$(az network vnet list --resource-group $BACKEND_RESOURCE_GROUP --query "[0].id" --output tsv)
 | |
| #     SUBNET_NAMES=$(az network vnet subnet list --resource-group $BACKEND_RESOURCE_GROUP --vnet-name $VNET_NAME --query "[].name" --output tsv)
 | |
| #     SUBNET_NAME="frontend"
 | |
| #     if [[ $SUBNET_NAMES == *$SUBNET_NAME* ]]; then
 | |
| #         echo "Subnet with name $SUBNET_NAME already exists"
 | |
| #     else
 | |
| #         echo "Subnet with name $SUBNET_NAME does not exist, creating one now."
 | |
| #         az network vnet subnet create --resource-group $BACKEND_RESOURCE_GROUP --vnet-name $VNET_NAME --name $SUBNET_NAME --address-prefixes 10.0.10.0/24
 | |
| #     fi
 | |
| #     az webapp vnet-integration add --name $WEB_APP --resource-group $RESOURCE_GROUP --vnet $VNET_ID --subnet $SUBNET_NAME
 | |
| # }
 | |
| 
 | |
| function usage() {
 | |
|    echo
 | |
|    echo "Usage: bash $0 [-h] -p <frontend_deploy.parameters.json>"
 | |
|    echo "Description: Deployment script for the Frontend App for GraphRAG Solution Accelerator."
 | |
|    echo "options:"
 | |
|    echo "  -h     Print this help menu."
 | |
|    echo "  -p     A JSON file containing the deployment parameters (frontend_deploy.parameters.json)."
 | |
|    echo
 | |
| }
 | |
| 
 | |
| function main() {
 | |
|     load_env_variables
 | |
|     populateRequiredParams $PARAMS_FILE
 | |
|     set_variables
 | |
|     create_resource_group
 | |
|     create_acr
 | |
|     build_and_push_image
 | |
|     create_app_service_plan
 | |
|     create_web_app_identity
 | |
|     configure_registry_credentials
 | |
|     create_web_app
 | |
|     configure_app_settings
 | |
|     create_federated_identity_credentials
 | |
|     configure_auth_settings
 | |
|     # add_vnet_integration
 | |
|     update_appreg_redirect_uris
 | |
|     restart_web_app
 | |
|     echo "**********Graphrag Frontend Web app deployment successful!**********"
 | |
|     echo "Please visit the webapp at https://$WEB_APP_URL"
 | |
|     echo "*******************************************************************"
 | |
| }
 | |
| 
 | |
| # print usage if no arguments are supplied
 | |
| [ $# -eq 0 ] && usage && exit 0
 | |
| PARAMS_FILE=""
 | |
| while getopts ":p:h" option; do
 | |
|     case "${option}" in
 | |
|         p)
 | |
|             PARAMS_FILE=${OPTARG}
 | |
|             ;;
 | |
|         h | *)
 | |
|             usage
 | |
|             exit 0
 | |
|             ;;
 | |
|     esac
 | |
| done
 | |
| shift $((OPTIND-1))
 | |
| # check if required arguments are supplied
 | |
| if [ ! -f $PARAMS_FILE ]; then
 | |
|     echo "Error: invalid required argument."
 | |
|     usage
 | |
|     exit 1
 | |
| fi
 | |
| 
 | |
| main
 | 
