diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84c42f17..d6e6a195 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -42,6 +42,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf7944c0..e60908b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b3bb3d4..c7fdc27d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,6 +72,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false @@ -158,6 +159,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false @@ -340,6 +342,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false @@ -360,6 +363,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false @@ -398,6 +402,7 @@ jobs: client-id: ${{ secrets.AZURE_CLIENT_ID }} tenant-id: ${{ secrets.AZURE_TENANT_ID }} subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + auth-type: IDENTITY enable-AzPSSession: true environment: azurecloud allow-no-subscriptions: false diff --git a/.gitignore b/.gitignore index 6045a074..4a0e118b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore +*_env.json + # User-specific files *.rsuser *.suo diff --git a/README.md b/README.md index 11bb2653..d9e65a6e 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,6 @@ Replace with your actual Azure AD Application Name. ```bash APP_NAME= azd env set AZURE_CLIENT_ID $(az ad app list --display-name $APP_NAME --query "[].appId" -otsv) -azd env set AZURE_CLIENT_PRINCIPAL_OID $(az ad sp list --display-name $APP_NAME --query "[].id" -otsv) -azd env set AZURE_CLIENT_SECRET $(az ad sp credential reset --id myServicePrincipalID --query "password" -otsv) ``` 3. Set Software Repository Location: @@ -230,6 +228,43 @@ azd env set ENABLE_BLOB_PUBLIC_ACCESS false Efficiently manage the resources with these Azure Developer CLI commands. They are designed to streamline the deployment process, allowing for a smooth setup and teardown of your environment. + +```mermaid +sequenceDiagram + participant Azd as user + participant Provision as command + participant Azure as azure + + + rect rgb(191, 223, 255) + alt + Note over Provision: featureCheck + Note over Provision: credCheck + end + Azd->>+Provision: azd provision + Provision->>Azure: arm deploy + Provision-->>-Azd: complete + alt + Note over Provision: softwareCheck + Note over Provision: entraAuth + end + end + + rect rgb(144,238,144) + alt + Note over Provision: firstUser + Note over Provision: refreshToken + end + Azd->>Provision: azd deploy + activate Provision + Provision-->>Azd: complete + deactivate Provision + alt + Note over Provision: settingsJson + end + end +``` + __Starting the Deployment__ To initiate the deployment, use the following command: @@ -238,7 +273,37 @@ To initiate the deployment, use the following command: azd provision ``` -This command starts the provisioning process, setting up all necessary resources in Azure according to your configuration. +This command starts the provisioning process, setting up all necessary resources in Azure according to your configuration. It involves a pre and post hook that peforms some additional automation. + +_Prehook_ + +1. Ensure that the subscription is configured with the required features. +2. Ensure a Client Secret is available for use. +3. Gather the Service Principal Object Id. + +_Posthook_ + +1. Ensure that the software installation is in compliance. +2. Ensure the AD Application has the Ingress Authentication Redirect URLs + + +__Deploy Initial Configuration__ + +Prior to running this command on the ingress url `https:///auth/` an authorization code can be easily retrieved to use in getting a refresh token for ease of use in calling APIs that require a bearer token. + +```bash +azd env set AUTH_CODE +azd deploy +azd env show AUTH_TOKEN +``` + +This command deploys some additional configuration helpful to using the solution. + +_Posthook_ + +1. Configure the Initial User into Entitlements. +2. Using a provided Authorization Code get an initial user refresh token. + __Removal and Cleaning up__ diff --git a/azure.yaml b/azure.yaml index 19887f76..f1318aab 100644 --- a/azure.yaml +++ b/azure.yaml @@ -7,25 +7,24 @@ infra: provider: bicep path: bicep hooks: - # preprovision: - # posix: - # interactive: false - # continueOnError: false - # shell: sh - # run: | - # # Check if PREPARE is not set to true - # if [[ "$PREPARE" == "true" ]]; then - # # Check if Docker command exists - # if command -v docker &> /dev/null; then - # echo "Building Docker Image" - # docker buildx build --no-cache -f scripts/Dockerfile-provision -t azd-provision scripts - # docker run --rm -v $(pwd):/workspace -v "${HOME}/.azure:/root/.azure" -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/preprovision.sh -s ${AZURE_SUBSCRIPTION_ID} - # sleep 5 - # else - # echo "Docker is not installed." - # exit 1 - # fi - # fi + preprovision: + posix: + interactive: false + continueOnError: false + shell: sh + run: | + # Check if PREPARE is not set to true + env_vars=$(azd env get-values | tr -d '"' | awk '{print "-e " $0}') + # Check if Docker command exists + if command -v docker &> /dev/null; then + echo "Building Docker Image" + docker buildx build --no-cache -f scripts/Dockerfile-provision -t azd-provision scripts + docker run --rm -v $(pwd):/workspace -v "${HOME}/.azure:/root/.azure" $env_vars -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/preprovision.sh -s ${AZURE_SUBSCRIPTION_ID} + sleep 5 + else + echo "Docker is not installed." + exit 1 + fi # windows: # interactive: false @@ -42,22 +41,55 @@ hooks: # docker run --rm -v ${pwd}:/workspace -v "$($HOME)/.azure:/root/.azure" -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/preprovision.sh -s $env:AZURE_SUBSCRIPTION_ID # Start-Sleep -Seconds 5 - # postprovision: - # posix: - # interactive: false - # continueOnError: false - # shell: sh - # run: | - # # Check if FETCH_ENV is not set to true - # if [[ "$FETCH_ENV" == "true" ]]; then - # # Check if Docker command exists - # if command -v docker &> /dev/null; then - # echo "Building Docker Image" - # docker buildx build --no-cache -f scripts/Dockerfile-provision -t azd-provision scripts - # docker run --rm -v $(pwd):/workspace -v "${HOME}/.azure:/root/.azure" -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/postprovision.sh -s ${AZURE_SUBSCRIPTION_ID} - # sleep 5 - # else - # echo "Docker is not installed." - # exit 1 - # fi - # fi \ No newline at end of file + postprovision: + posix: + interactive: false + continueOnError: false + shell: sh + run: | + env_vars=$(azd env get-values | tr -d '"' | awk '{print "-e " $0}') + # Check if Docker command exists + if command -v docker &> /dev/null; then + echo "Building Docker Image" + docker buildx build --no-cache -f scripts/Dockerfile-provision -t azd-provision scripts + docker run --rm -v $(pwd):/workspace -v "${HOME}/.azure:/root/.azure" $env_vars -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/postprovision.sh -s ${AZURE_SUBSCRIPTION_ID} + sleep 5 + else + echo "Docker is not installed." + exit 1 + fi + + predeploy: + posix: + interactive: false + continueOnError: false + shell: sh + run: | + env_vars=$(azd env get-values | tr -d '"' | awk '{print "-e " $0}') + # Check if Docker command exists + if command -v docker &> /dev/null; then + echo "Building Docker Image" + docker buildx build --no-cache -f scripts/Dockerfile-provision -t azd-provision scripts + docker run --rm -v $(pwd):/workspace -v "${HOME}/.azure:/root/.azure" $env_vars -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/predeploy.sh -s ${AZURE_SUBSCRIPTION_ID} + sleep 5 + else + echo "Docker is not installed." + exit 1 + fi + postdeploy: + posix: + interactive: false + continueOnError: false + shell: sh + run: | + env_vars=$(azd env get-values | tr -d '"' | awk '{print "-e " $0}') + # Check if Docker command exists + if command -v docker &> /dev/null; then + echo "Building Docker Image" + docker buildx build --no-cache -f scripts/Dockerfile-provision -t azd-provision scripts + docker run --rm -v $(pwd):/workspace -v "${HOME}/.azure:/root/.azure" $env_vars -e AZURE_CONFIG_DIR=/root/.azure azd-provision /usr/local/bin/postdeploy.sh -s ${AZURE_SUBSCRIPTION_ID} + sleep 5 + else + echo "Docker is not installed." + exit 1 + fi \ No newline at end of file diff --git a/bicep/main.bicep b/bicep/main.bicep index 0e210c1a..11cf24e8 100644 --- a/bicep/main.bicep +++ b/bicep/main.bicep @@ -438,5 +438,6 @@ module serviceBlade 'modules/blade_service.bicep' = { output KEYVAULT_NAME string = commonBlade.outputs.keyvaultName output ACR_NAME string = serviceBlade.outputs.registryName +output AKS_NAME string = serviceBlade.outputs.clusterName //ACSCII Art link : https://textkool.com/en/ascii-art-generator?hl=default&vl=default&font=Star%20Wars&text=changeme diff --git a/bicep/modules/blade_service.bicep b/bicep/modules/blade_service.bicep index 42c6b656..6e092d5f 100644 --- a/bicep/modules/blade_service.bicep +++ b/bicep/modules/blade_service.bicep @@ -767,3 +767,6 @@ module grafana 'aks_grafana.bicep' = if(enableMonitoring){ @description('The name of the container registry.') output registryName string = registry.outputs.name + +@description('The name of the cluster.') +output clusterName string = cluster.outputs.aksClusterName diff --git a/charts/osdu-developer-auth/templates/config-map.yaml b/charts/osdu-developer-auth/templates/config-map.yaml index ac8b0a85..ad6ccc3d 100644 --- a/charts/osdu-developer-auth/templates/config-map.yaml +++ b/charts/osdu-developer-auth/templates/config-map.yaml @@ -147,7 +147,7 @@ data: return "https://login.microsoftonline.com/" + this.clientId + "/oauth2/logout"; }, redirect: function () { - return location.protocol + '//' + location.host + location.pathname + return 'https://' + location.host + location.pathname } }, beforeMount: function () { diff --git a/docs/pipelines.md b/docs/pipelines.md index 11442d02..02e0e57e 100644 --- a/docs/pipelines.md +++ b/docs/pipelines.md @@ -58,6 +58,7 @@ This action ensures that the bicep can build properly. The release action will run whenever a release is created to ensure we have a copy of the ARM template from that release that could then be used by other systems as necessary. + ```mermaid sequenceDiagram participant Workflow as "GitHub Workflow" diff --git a/scripts/Dockerfile-provision b/scripts/Dockerfile-provision index d45ff0fc..3d4ac47a 100644 --- a/scripts/Dockerfile-provision +++ b/scripts/Dockerfile-provision @@ -1,15 +1,30 @@ FROM mcr.microsoft.com/azure-cli:latest # Install OS packages -RUN apk add --no-cache curl ncurses +RUN apk --no-cache update && \ + apk add --no-cache \ + curl \ + ncurses \ + # build dependencies for native binaries in Python packages + gcc \ + make \ + openssl-dev \ + libffi-dev \ + musl-dev \ + python3-dev # Install azd RUN curl -fsSL https://aka.ms/install-azd.sh | bash +RUN pip3 install --no-cache-dir --upgrade pip setuptools wheel httpie \ + && pip3 install --no-cache-dir --force-reinstall pycryptodome + # Copy the script into the container COPY functions.sh /usr/local/bin/functions.sh COPY hook-postprovision.sh /usr/local/bin/postprovision.sh COPY hook-preprovision.sh /usr/local/bin/preprovision.sh +COPY hook-predeploy.sh /usr/local/bin/predeploy.sh +COPY hook-postdeploy.sh /usr/local/bin/postdeploy.sh # Create a directory to work in WORKDIR /workspace @@ -17,7 +32,9 @@ WORKDIR /workspace # Make the script executable RUN chmod +x /usr/local/bin/functions.sh \ && chmod +x /usr/local/bin/postprovision.sh \ - && chmod +x /usr/local/bin/preprovision.sh + && chmod +x /usr/local/bin/preprovision.sh \ + && chmod +x /usr/local/bin/predeploy.sh \ + && chmod +x /usr/local/bin/postdeploy.sh # Set the entrypoint to run your script CMD ["/bin/bash"] diff --git a/scripts/hook-postdeploy.sh b/scripts/hook-postdeploy.sh new file mode 100644 index 00000000..93dc4e5a --- /dev/null +++ b/scripts/hook-postdeploy.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +############################################################################################### +# ---------------------------- # +# postDeploy - Post Deploy # +# ---------------------------- # +# # +# Usage: ./hook-postdeploy.sh # +# # +# Prerequisites: # +# 1. Ensure you have Azure CLI installed, and you're logged in to Azure CLI. # +# # +# Options: # +# -s : Specify a particular subscriptionId to use. # +# -h : Print help message and exit # +# # +# Note: # +# You must provide a subscription ID # +# # +############################################################################################### + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +PARENT_DIR=`dirname $SCRIPT_DIR` +ROOT_DIR=`dirname $PARENT_DIR` + +if [[ $SCRIPT_DIR == "/usr/local/bin" ]] +then + ROOT_DIR="/workspace" +else + ROOT_DIR=`dirname $PARENT_DIR` +fi + +print_help() { + echo -e "Usage: $0 --subscription SUBSCRIPTION_ID\n" + echo -e "Options:" + echo -e " -s Set the subscription ID" + echo -e " -h Print this help message and exit" + echo -e "\nYou must provide a SubscriptionId." +} + +# Parsing command-line arguments +AZURE_SUBSCRIPTION="" +while getopts ":hs:" opt; do + case ${opt} in + h ) + print_help + exit 0 + ;; + s ) + AZURE_SUBSCRIPTION=$OPTARG + ;; + \? ) + echo "Invalid option: -$OPTARG" >&2 + print_help + exit 1 + ;; + : ) + echo "Option -$OPTARG requires an argument." >&2 + print_help + exit 1 + ;; + esac +done +shift $((OPTIND -1)) + +############################### +# Checks +if [[ -z "$AZURE_SUBSCRIPTION" ]]; +then + echo "Error: You must provide a SubscriptionId" >&2 + print_help + exit 1 +fi + +# Check Azure CLI version. +REQUIRED_AZ_CLI_VERSION="2.58.0" +CURRENT_AZ_CLI_VERSION="$(az --version | head -n 1 | awk -F' ' '{print $2}')" + +if [[ $(echo -e "$REQUIRED_AZ_CLI_VERSION\n$CURRENT_AZ_CLI_VERSION"|sort -V|head -n1) != $REQUIRED_AZ_CLI_VERSION ]]; then + echo "This script requires Azure CLI version $REQUIRED_AZ_CLI_VERSION or higher. You have version $CURRENT_AZ_CLI_VERSION." + exit 1 +fi + +############################### +# Get Access Token using Refresh Token +if [[ -n $AUTH_REFRESH ]]; then + echo "Getting a Access Token using the Refresh Token..." + + response=$(curl --request POST \ + --url https://login.microsoftonline.com/${TENANT_ID}/oauth2/v2.0/token \ + --header "content-type: application/x-www-form-urlencoded" \ + --data "grant_type=refresh_token" \ + --data "client_id=${AZURE_CLIENT_ID}" \ + --data "client_secret=$AZURE_CLIENT_SECRET" \ + --data refresh_token=$REFAUTH_TOKENESH_TOKEN \ + --data "scope=${AZURE_CLIENT_ID}/.default openid profile offline_access") + + # Extract the Refresh Token from the body and set it as an environment variable + refresh_token=$(echo "$response" | jq -r '.refresh_token') + if [[ -n $refresh_token ]]; then + azd env set AUTH_REFRESH $refresh_token + fi +fi + +output=$(azd env get-values) +AZURE_RESOURCE_GROUP=$(echo "$output" | grep "AZURE_RESOURCE_GROUP" | cut -d'=' -f2 | tr -d '"') +AZURE_TENANT_ID=$(echo "$output" | grep "AZURE_TENANT_ID" | cut -d'=' -f2 | tr -d '"') +AZURE_CLIENT_ID=$(echo "$output" | grep "AZURE_CLIENT_ID" | cut -d'=' -f2 | tr -d '"') +AZURE_CLIENT_SECRET=$(echo "$output" | grep "AZURE_CLIENT_SECRET" | cut -d'=' -f2 | tr -d '"') +AUTH_INGRESS=$(echo "$output" | grep "AUTH_INGRESS" | cut -d'=' -f2 | tr -d '"') +AUTH_REFRESH=$(echo "$output" | grep "AUTH_REFRESH" | cut -d'=' -f2 | tr -d '"') + +mkdir -p .vscode +cat << EOF > ".vscode/settings.json" +{ + "rest-client.environmentVariables": { + "${AZURE_RESOURCE_GROUP}": { + "TENANT_ID": "${AZURE_TENANT_ID}", + "CLIENT_ID": "${AZURE_CLIENT_ID}", + "CLIENT_SECRET": "${AZURE_CLIENT_SECRET}", + "HOST": "${AUTH_INGRESS}", + "REFRESH_TOKEN": "${AUTH_REFRESH}", + "DATA_PARTITION": "opendes" + } + } +} +EOF diff --git a/scripts/hook-postprovision.sh b/scripts/hook-postprovision.sh index 82bc21c2..cf163919 100644 --- a/scripts/hook-postprovision.sh +++ b/scripts/hook-postprovision.sh @@ -82,9 +82,59 @@ if [[ $(echo -e "$REQUIRED_AZ_CLI_VERSION\n$CURRENT_AZ_CLI_VERSION"|sort -V|head fi ############################### -# Require Common Functions -if [[ -f "$SCRIPT_DIR/functions.sh" ]]; then - source "$SCRIPT_DIR/functions.sh" +# Checking Flux Compliance +echo "Checking Software Installation..." + +# Initialize timer +end=$((SECONDS+600)) # 600 seconds = 10 minutes + +# Loop to check Flux compliance every 30 seconds up to 10 minutes +while [ $SECONDS -lt $end ]; do + + compliance_state=$(az k8s-configuration flux show -t managedClusters -g $AZURE_RESOURCE_GROUP --cluster-name $AKS_NAME --name flux-system --query 'complianceState' -otsv) + + echo "Current Software State: $compliance_state" + + if [ "$compliance_state" == "Compliant" ]; then + echo "Software has been installed." + break + else + echo "Software still installing, retrying in 30 seconds." + sleep 30 + fi +done + +if [ $SECONDS -ge $end ]; then + echo "Software check timed out after 10 minutes." fi -env \ No newline at end of file + +############################### +# Add Redirect URIs to Azure AD App +redirect_uris=() # Initialize an empty array to hold the redirect URIs + +# Fetch Node Resource Group from AKS Cluster +node_resource_group=$(az aks show -g $AZURE_RESOURCE_GROUP -n $AKS_NAME --query nodeResourceGroup -o tsv) + +# Fetch Public IP Address of the Load Balancer named 'kubernetes' +public_ip=$(az network public-ip list -g "$node_resource_group" --query "[?contains(name, 'kubernetes')].ipAddress" -otsv) +if [[ -n $public_ip ]]; then + echo "Adding Public Web Endpoint:" + redirect_uris+=("https://$public_ip/auth/") # Add public ingress URI +fi + +# Fetch Private IP Address from the Load Balancer named 'kubernetes-internal' +private_ip=$(az network lb frontend-ip list --lb-name kubernetes-internal -g "$node_resource_group" --query [].privateIPAddress -otsv) +if [[ -n $private_ip ]]; then + echo "Adding Public Web Endpoint:" + redirect_uris+=("https://$private_ip/auth/") # Add private ingress URI +fi + + +# Update Azure AD app only if there are URIs to add +if [ ${#redirect_uris[@]} -gt 0 ]; then + echo "==================================================================" + echo "Adding Web Direct URIs" + echo "==================================================================" + az ad app update --id $AZURE_CLIENT_ID --web-redirect-uris "${redirect_uris[@]}" +fi diff --git a/scripts/hook-predeploy.sh b/scripts/hook-predeploy.sh new file mode 100644 index 00000000..1ed0bb0e --- /dev/null +++ b/scripts/hook-predeploy.sh @@ -0,0 +1,215 @@ +#!/bin/bash + +############################################################################################### +# ---------------------------- # +# preDeploy - Pre Deploy # +# ---------------------------- # +# # +# Usage: ./hook-predeploy.sh # +# # +# Prerequisites: # +# 1. Ensure you have Azure CLI installed, and you're logged in to Azure CLI. # +# # +# Options: # +# -s : Specify a particular subscriptionId to use. # +# -h : Print help message and exit # +# # +# Note: # +# You must provide a subscription ID # +# # +############################################################################################### + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +PARENT_DIR=`dirname $SCRIPT_DIR` +ROOT_DIR=`dirname $PARENT_DIR` + +if [[ $SCRIPT_DIR == "/usr/local/bin" ]] +then + ROOT_DIR="/workspace" +else + ROOT_DIR=`dirname $PARENT_DIR` +fi + +print_help() { + echo -e "Usage: $0 --subscription SUBSCRIPTION_ID\n" + echo -e "Options:" + echo -e " -s Set the subscription ID" + echo -e " -h Print this help message and exit" + echo -e "\nYou must provide a SubscriptionId." +} + +# Parsing command-line arguments +AZURE_SUBSCRIPTION="" +while getopts ":hs:" opt; do + case ${opt} in + h ) + print_help + exit 0 + ;; + s ) + AZURE_SUBSCRIPTION=$OPTARG + ;; + \? ) + echo "Invalid option: -$OPTARG" >&2 + print_help + exit 1 + ;; + : ) + echo "Option -$OPTARG requires an argument." >&2 + print_help + exit 1 + ;; + esac +done +shift $((OPTIND -1)) + +############################### +# Checks +if [[ -z "$AZURE_SUBSCRIPTION" ]]; +then + echo "Error: You must provide a SubscriptionId" >&2 + print_help + exit 1 +fi + +# Check Azure CLI version. +REQUIRED_AZ_CLI_VERSION="2.58.0" +CURRENT_AZ_CLI_VERSION="$(az --version | head -n 1 | awk -F' ' '{print $2}')" + +if [[ $(echo -e "$REQUIRED_AZ_CLI_VERSION\n$CURRENT_AZ_CLI_VERSION"|sort -V|head -n1) != $REQUIRED_AZ_CLI_VERSION ]]; then + echo "This script requires Azure CLI version $REQUIRED_AZ_CLI_VERSION or higher. You have version $CURRENT_AZ_CLI_VERSION." + exit 1 +fi + +if [[ ! -n $AZURE_TENANT_ID ]]; then + AZURE_TENANT_ID=$(az account show --query tenantId -o tsv) + azd env set AZURE_TENANT_ID $AZURE_TENANT_ID +fi + +############################### +# Add Ingress +if [[ ! -n $AUTH_INGRESS ]]; then + echo "Fetching Ingress IP Address..." + + # Fetch Node Resource Group from AKS Cluster + node_group=$(az aks show -g $AZURE_RESOURCE_GROUP -n $AKS_NAME --query nodeResourceGroup -o tsv) + if [[ -n "$INGRESS" && "$INGRESS" == 'internal' ]]; then + AUTH_INGRESS="https://$(az network lb frontend-ip list --lb-name kubernetes-internal -g "$node_group" --query '[].privateIPAddress' -o tsv)" + else + AUTH_INGRESS="https://$(az network public-ip list -g "$node_group" --query "[?contains(name, 'kubernetes')].ipAddress" -o tsv)" + fi + + azd env set AUTH_INGRESS $AUTH_INGRESS +fi + + +############################### +# Add the first user. +if [[ ! -n $AUTH_USER ]]; then + echo "Adding the first user..." + + ACCESS_TOKEN=$(curl --request POST \ + --url https://login.microsoftonline.com/${AZURE_TENANT_ID}/oauth2/v2.0/token \ + --header "content-type: application/x-www-form-urlencoded" \ + --data "grant_type=client_credentials" \ + --data "client_id=${AZURE_CLIENT_ID}" \ + --data "client_secret=${AZURE_CLIENT_SECRET}" \ + --data "scope=${AZURE_CLIENT_ID}/.default" |jq -r .access_token) + +### + AUTH_USER=$(az ad signed-in-user show --query userPrincipalName -o tsv) + json_payload=$(jq -n --arg email "$AUTH_USER" '{"email": $email, "role": "MEMBER"}') + + # Add the first user. + response=$(curl -s -w "%{http_code}" -X POST "${AUTH_INGRESS}/api/entitlements/v2/groups/users@opendes.contoso.com/members" \ + --insecure \ + -H "Authorization: Bearer ${ACCESS_TOKEN}" \ + -H "Accept: application/json" \ + -H "data-partition-id: opendes" \ + -H "Content-Type: application/json" \ + -d "$json_payload") + + + # Extract HTTP status code from the last three characters + http_status_code=$(echo "$response" | grep -oE '[0-9]{3}$') + + # Remove the last three characters (HTTP status code) to isolate the body + body=${response%???} + + + # Check the response code and act accordingly + if [ "$http_status_code" -eq 200 ] || [ "$http_status_code" -eq 201 ] || [ "$http_status_code" -eq 409 ]; then + echo "Request successful." + else + echo "Request failed with status $http_status_code. Body: $body" + exit 1 + fi + + # Assign the Ops role to the user. + echo "Assigning the Ops role to the user..." + response=$(curl -s -w "%{http_code}" -X POST "${AUTH_INGRESS}/api/entitlements/v2/groups/users.datalake.ops@opendes.contoso.com/members" \ + --insecure \ + -H "accept: application/json" \ + -H "content-type: application/json" \ + -H "authorization: Bearer ${ACCESS_TOKEN}" \ + -H "data-partition-id: opendes" \ + -d "$json_payload") + + # Extract HTTP status code from the last three characters + http_status_code=$(echo "$response" | grep -oE '[0-9]{3}$') + + # Remove the last three characters (HTTP status code) to isolate the body + body=${response%???} + + # Check the response code and act accordingly + if [ "$http_status_code" -eq 200 ] || [ "$http_status_code" -eq 201 ] || [ "$http_status_code" -eq 409 ]; then + echo "Request successful." + else + echo "Request failed with status $http_status_code. Body: $body" + exit 1 + fi + + azd env set AUTH_USER $AUTH_USER +fi + + +############################### +# Get Refresh Token using Authorization Code +if [[ -z "$AUTH_REFRESH" ]]; then + if [[ -z "$AUTH_CODE" ]]; then + echo "Error: Neither AUTH_CODE nor AUTH_REFRESH is available." + exit 1 + else + echo "Getting a Refresh Token using the Authorization Code..." + + response=$(curl -s -w "%{http_code}" --request POST \ + --url https://login.microsoftonline.com/${AZURE_TENANT_ID}/oauth2/v2.0/token \ + --header "Content-Type: application/x-www-form-urlencoded" \ + --data-urlencode "grant_type=authorization_code" \ + --data-urlencode "redirect_uri=$AUTH_INGRESS/auth/" \ + --data-urlencode "client_id=$AZURE_CLIENT_ID" \ + --data-urlencode "client_secret=$AZURE_CLIENT_SECRET" \ + --data-urlencode "scope=$AZURE_CLIENT_ID/.default openid profile offline_access" \ + --data-urlencode "code=$AUTH_CODE") + + # Extract HTTP status code from the last three characters + http_status_code=$(echo "$response" | grep -oE '[0-9]{3}$') + + # Remove the last three characters (HTTP status code) to isolate the body + body=${response%???} + + # Check the response code and act accordingly + if [ "$http_status_code" -eq 200 ]; then + echo "Request successful." + # Set the refresh token and void the auth code. + refresh_token=$(echo "$body" | jq -r '.refresh_token') + azd env set AUTH_REFRESH $refresh_token + azd env set AUTH_CODE "" + else + echo "Request failed with status $http_status_code. Body: $body" + exit 1 + fi + fi +fi + + diff --git a/scripts/hook-preprovision.sh b/scripts/hook-preprovision.sh index 49fe1224..29c4a780 100755 --- a/scripts/hook-preprovision.sh +++ b/scripts/hook-preprovision.sh @@ -118,17 +118,9 @@ fi # Registering AKS feature extensions aksExtensions=( - "PodSecurityPolicyPreview" - "KubeletDisk" - "AKS-KedaPreview" "RunCommandPreview" "EnablePodIdentityPreview" - "UserAssignedIdentityPreview" - "EnablePrivateClusterPublicFQDN" "PodSubnetPreview" - "AKS-VPAPreview" - "AzureOverlayPreview" - "KubeProxyConfigurationPreview" ) @@ -174,4 +166,22 @@ if [[ $ok == 1 ]]; then PrintMessage " Refreshing the registration of the Microsoft.ContainerService resource provider..." az provider register --namespace Microsoft.ContainerService PrintMessage " Microsoft.ContainerService resource provider registration successfully refreshed" -fi \ No newline at end of file +fi + +if [[ -z $AZURE_CLIENT_ID ]]; then + PrintMessage "==================================================================" 4 + PrintMessage "********ERROR********* --> Missing AZURE_CLIENT_ID" 4 + PrintMessage "==================================================================" 4 + exit 1 +fi + + +if [[ -z $AZURE_CLIENT_PRINCIPAL_OID ]]; then + PrintMessage " Retrieving AZURE_CLIENT_PRINCIPAL_OID..." + azd env set AZURE_CLIENT_PRINCIPAL_OID $(az ad sp show --id $AZURE_CLIENT_ID --query "id" -otsv) +fi + +if [[ -z $AZURE_CLIENT_SECRET ]]; then + PrintMessage " Retrieving AZURE_CLIENT_SECRET..." + azd env set AZURE_CLIENT_SECRET $(az ad app credential reset --id $AZURE_CLIENT_ID --query password -otsv) +fi diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index 27377ac2..00000000 --- a/tools/README.md +++ /dev/null @@ -1 +0,0 @@ -# Rest Test Scripts \ No newline at end of file diff --git a/tools/rest-scripts/auth-token.http b/tools/rest-scripts/auth-token.http new file mode 100644 index 00000000..6240d88e --- /dev/null +++ b/tools/rest-scripts/auth-token.http @@ -0,0 +1,44 @@ +# -------HTTP REST CLIENT ------- +# https://marketplace.visualstudio.com/items?itemName=humao.rest-client + +@login_base = login.microsoftonline.com/{{TENANT_ID}} +@oauth_token_host = {{login_base}}/oauth2/v2.0/token +@scopes = {{CLIENT_ID}}/.default openid profile offline_access +@AUTH_CODE = + +# ----------------------- +# OAUTH authorization_code +# ----------------------- +### +# @name authorize +POST https://{{oauth_token_host}} HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +grant_type=authorization_code +&redirect_uri=http://localhost:8080 +&client_id={{CLIENT_ID}} +&client_secret={{CLIENT_SECRET}} +&scope={{scopes}} +&code={{AUTH_CODE}} + + +# ----------------------- +# OAUTH refresh_token +# ----------------------- +### +# @name refresh +POST https://{{oauth_token_host}} HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +grant_type=refresh_token +&client_id={{CLIENT_ID}} +&client_secret={{CLIENT_SECRET}} +&refresh_token={{authorize.response.body.refresh_token}} +&scope={{scopes}} + + +# ----------------------- +# API (Variables) +# ----------------------- +### +@access_token = {{refresh.response.body.access_token}} diff --git a/tools/rest-scripts/entitlement.http b/tools/rest-scripts/entitlement.http index b7a6a6c9..ca8304d7 100644 --- a/tools/rest-scripts/entitlement.http +++ b/tools/rest-scripts/entitlement.http @@ -38,8 +38,7 @@ grant_type=refresh_token # ----------------------- ### @access_token = {{refresh.response.body.access_token}} -@ENDPOINT = https://{{HOST}} -@ENTITLEMENTS_HOST = {{ENDPOINT}}/api/entitlements/v2 +@ENTITLEMENTS_HOST = {{HOST}}/api/entitlements/v2 # ----------------------- diff --git a/tools/rest-scripts/legal.http b/tools/rest-scripts/legal.http new file mode 100644 index 00000000..68bf7a8e --- /dev/null +++ b/tools/rest-scripts/legal.http @@ -0,0 +1,123 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# -------HTTP REST CLIENT ------- +# https://marketplace.visualstudio.com/items?itemName=humao.rest-client +# +# Purpose: Sample requests for Legal Service + +# ----------------------- +# OAUTH (Variables) +# ----------------------- +### +@login_base = login.microsoftonline.com/{{TENANT_ID}} +@oauth_token_host = {{login_base}}/oauth2/v2.0/token +@scopes = {{CLIENT_ID}}/.default openid profile offline_access + + +# ----------------------- +# OAUTH refresh_token +# ----------------------- +### +# @name refresh +POST https://{{oauth_token_host}} HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +grant_type=refresh_token +&client_id={{CLIENT_ID}} +&client_secret={{CLIENT_SECRET}} +&refresh_token={{REFRESH_TOKEN}} +&scope={{scopes}} + + +# ----------------------- +# API (Variables) +# ----------------------- +### +@access_token = {{refresh.response.body.access_token}} +@LEGAL_HOST = {{HOST}}/api/legal/v1 +@tag = legal-tag-load + +# ----------------------- +# API: Version +# ----------------------- +### +# @name info +GET {{LEGAL_HOST}}/info +Authorization: Bearer {{access_token}} +Accept: application/json + + +# ----------------------- +# API: legal +# ----------------------- +### +# @name getTagProperties +GET {{LEGAL_HOST}}/legaltags:properties +Authorization: Bearer {{access_token}} +Accept: application/json +data-partition-id: {{DATA_PARTITION}} + + +### +# @name getAllTag +GET {{LEGAL_HOST}}/legaltags +Authorization: Bearer {{access_token}} +Accept: application/json +data-partition-id: {{DATA_PARTITION}} + + +### +# @name create_tag +POST {{LEGAL_HOST}}/legaltags +Authorization: Bearer {{access_token}} +Content-Type: application/json +data-partition-id: {{DATA_PARTITION}} + +{ + "name": "{{tag}}", + "description": "This is a TNO Data load tag", + "properties": { + "countryOfOrigin": [ + "US" + ], + "contractId": "A1234", + "expirationDate": "2023-12-31", + "originator": "MyCompany", + "dataType": "Transferred Data", + "securityClassification": "Public", + "personalData": "No Personal Data", + "exportClassification": "EAR99" + } +} + + +### +# @name getTag +GET {{LEGAL_HOST}}/legaltags/{{DATA_PARTITION}}-{{tag}} +Authorization: Bearer {{access_token}} +Accept: application/json +data-partition-id: {{DATA_PARTITION}} + + +### +# @name updateTag +PUT {{LEGAL_HOST}}/legaltags +Authorization: Bearer {{access_token}} +Content-Type: application/json +data-partition-id: {{DATA_PARTITION}} + +{ + "name": "{{DATA_PARTITION}}-{{tag}}", + "contractId": "A1234", + "description": "Updated: This is a test tag from Rest Scripts", + "expirationDate": "2025-12-25" +} + + +### +# @name deleteTag +DELETE {{LEGAL_HOST}}/legaltags/{{DATA_PARTITION}}-{{tag}} +Authorization: Bearer {{access_token}} +Accept: application/json +data-partition-id: {{DATA_PARTITION}}