#!/usr/bin/env bash # Copyright (c) Microsoft Corporation. # Licensed under the MIT license. PRJ_ROOT="$(cd `dirname "${BASH_SOURCE}"`/../..; pwd)" ENV_CODE=${1:-${ENV_CODE}} LE_EMAIL_ADDRESS=${2:-${LE_EMAIL_ADDRESS}} [[ -z "$ENV_CODE" ]] && { echo "Environment Code value not supplied"; exit 1; } [[ -z "$LE_EMAIL_ADDRESS" ]] && { echo "Let's Encrypt e-mail address (LE_EMAIL_ADDRESS) not specified"; exit 1; } set -a set -e ENV_NAME=${ENV_NAME:-"stac-${ENV_CODE}"} MONITORING_RESOURCE_GROUP=${MONITORING_RESOURCE_GROUP:-"${ENV_CODE}-monitoring-rg"} VNET_RESOURCE_GROUP=${VNET_RESOURCE_GROUP:-"${ENV_CODE}-vnet-rg"} DATA_RESOURCE_GROUP=${DATA_RESOURCE_GROUP:-"${ENV_CODE}-data-rg"} PROCESSING_RESOURCE_GROUP=${PROCESSING_RESOURCE_GROUP:-"${ENV_CODE}-processing-rg"} UAMI=${UAMI:-"${ENV_CODE}-stacaks-mi"} AKS_NAMESPACE=${AKS_NAMESPACE:-"pgstac"} AKS_INGRESS_NAMESPACE=${AKS_INGRESS_NAMESPACE:-"ingress"} ENV_LABEL=${ENV_LABEL:-"stacpool"} # aks agent pool name to deploy kubectl deployment yaml files AKS_SERVICE_ACCOUNT_NAME=${AKS_SERVICE_ACCOUNT_NAME:-'stac-svc-acct'} FEDERATED_IDENTITY_NAME="stacaksfederatedidentity" SUBSCRIPTION=$(az account show --query id -o tsv) AZURE_APP_INSIGHTS=$(az resource list -g $MONITORING_RESOURCE_GROUP --resource-type "Microsoft.Insights/components" \ --query "[?tags.environment && tags.environment == '$ENV_NAME'].name" -o tsv) AZURE_LOG_CONNECTION_STRING=$(az resource show \ -g $MONITORING_RESOURCE_GROUP \ --resource-type Microsoft.Insights/components \ -n ${AZURE_APP_INSIGHTS} \ --query "properties.ConnectionString" -o tsv) DATA_STORAGE_ACCOUNT_NAME=$(az storage account list \ --query "[?tags.store && tags.store == 'data'].name" -o tsv -g ${DATA_RESOURCE_GROUP}) DATA_STORAGE_ACCOUNT_KEY=$(az storage account keys list \ --account-name ${DATA_STORAGE_ACCOUNT_NAME} --resource-group ${DATA_RESOURCE_GROUP} \ --query "[0].value" -o tsv) DATA_STORAGE_ACCOUNT_CONNECTION_STRING=$(az storage account show-connection-string \ --resource-group $DATA_RESOURCE_GROUP \ --name $DATA_STORAGE_ACCOUNT_NAME \ | jq -r ".connectionString") DATA_STORAGE_ACCOUNT_BLOB_ENDPOINT=$(az storage account show -n $DATA_STORAGE_ACCOUNT_NAME \ --query primaryEndpoints.blob -otsv) DATA_STORAGE_ACCOUNT_BLOB_DOMAIN=${DATA_STORAGE_ACCOUNT_BLOB_ENDPOINT#https://} DATA_STORAGE_ACCOUNT_BLOB_DOMAIN=${DATA_STORAGE_ACCOUNT_BLOB_DOMAIN%%/*} AKS_RESOURCE_GROUP=${AKS_RESOURCE_GROUP:-${PROCESSING_RESOURCE_GROUP}} AKS_CLUSTER_NAME=$(az aks list -g ${AKS_RESOURCE_GROUP} \ --query "[?tags.type && tags.type == 'k8s'].name" -otsv) ACR_DNS=$(az acr list -g ${PROCESSING_RESOURCE_GROUP} \ --query "[?tags.environment && tags.environment == '$ENV_NAME'].loginServer" -otsv) AKS_OIDC_ISSUER="$(az aks show --resource-group ${AKS_RESOURCE_GROUP} \ --name ${AKS_CLUSTER_NAME} --query "oidcIssuerProfile.issuerUrl" -o tsv)" AKS_POD_CIDR="$(az aks show -g $AKS_RESOURCE_GROUP -n $AKS_CLUSTER_NAME \ --query networkProfile.podCidr -o tsv)" USER_ASSIGNED_CLIENT_ID="$(az identity show -g ${PROCESSING_RESOURCE_GROUP} \ --name $UAMI --query 'clientId' -o tsv)" IDENTITY_TENANT=$(az aks show --name ${AKS_CLUSTER_NAME} \ --resource-group ${PROCESSING_RESOURCE_GROUP} --query identity.tenantId -o tsv) az identity federated-credential create --name ${FEDERATED_IDENTITY_NAME} \ --identity-name $UAMI \ --resource-group ${PROCESSING_RESOURCE_GROUP} \ --issuer ${AKS_OIDC_ISSUER} \ --subject system:serviceaccount:${AKS_NAMESPACE}:${AKS_SERVICE_ACCOUNT_NAME} \ -o none SERVICE_BUS_NAMESPACE=$(az servicebus namespace list \ -g ${DATA_RESOURCE_GROUP} --query "[?tags.environment && tags.environment == '$ENV_NAME'].name" -otsv) STAC_METADATA_TYPE_NAME=${STAC_METADATA_TYPE_NAME:-"fgdc"} COLLECTION_ID=${COLLECTION_ID:-"naip"} JPG_EXTENSION=${JPG_EXTENSION:-"200.jpg"} XML_EXTENSION=${XML_EXTENSION:-"aux.xml"} REPLICAS=${REPLICAS:-"3"} POD_CPU=${POD_CPU:-"0.5"} POD_MEMORY=${POD_MEMORY:-"2Gi"} GENERATE_STAC_JSON_IMAGE_NAME=${GENERATE_STAC_JSON_IMAGE_NAME:-"generate-stac-json"} DATA_STORAGE_PGSTAC_CONTAINER_NAME=${DATA_STORAGE_PGSTAC_CONTAINER_NAME:-"pgstac"} ENV_LABLE=${ENV_LABLE:-"stacpool"} # aks agent pool name to deploy kubectl deployment yaml files SERVICE_BUS_AUTH_POLICY_NAME=${SERVICE_BUS_AUTH_POLICY_NAME:-"RootManageSharedAccessKey"} SERVICE_BUS_CONNECTION_STRING=$(az servicebus namespace authorization-rule keys list \ --resource-group ${DATA_RESOURCE_GROUP} \ --namespace-name ${SERVICE_BUS_NAMESPACE} \ --name ${SERVICE_BUS_AUTH_POLICY_NAME} \ --query "primaryConnectionString" -otsv) STAC_EVENT_CONSUMER_IMAGE_NAME=${STAC_EVENT_CONSUMER_IMAGE_NAME:-"stac-event-consumer"} PGSTAC_SERVICE_BUS_TOPIC_NAME=${PGSTAC_SERVICE_BUS_TOPIC_NAME:-"pgstactopic"} PGSTAC_SERVICE_BUS_TOPIC_AUTH_POLICY_NAME=${PGSTAC_SERVICE_BUS_TOPIC_AUTH_POLICY_NAME:-"pgstacpolicy"} PGSTAC_SERVICE_BUS_SUBSCRIPTION_NAME=${PGSTAC_SERVICE_BUS_SUBSCRIPTION_NAME:-"pgstacsubscription"} PGSTAC_SERVICE_BUS_CONNECTION_STRING=$(az servicebus topic authorization-rule keys list \ --resource-group ${DATA_RESOURCE_GROUP} \ --namespace-name ${SERVICE_BUS_NAMESPACE} \ --topic ${PGSTAC_SERVICE_BUS_TOPIC_NAME} \ --name ${PGSTAC_SERVICE_BUS_TOPIC_AUTH_POLICY_NAME} \ --query "primaryConnectionString" -otsv) GENERATED_STAC_STORAGE_CONTAINER_NAME=${GENERATED_STAC_STORAGE_CONTAINER_NAME:-"generatedstacjson"} KEY_VAULT_NAME=$(az keyvault list --query "[?tags.environment && tags.environment == '$ENV_NAME'].name" -o tsv -g $DATA_RESOURCE_GROUP) PGHOST=$(az postgres flexible-server list --resource-group $DATA_RESOURCE_GROUP --query '[].fullyQualifiedDomainName' -o tsv) PGHOSTONLY=$(az postgres flexible-server list --resource-group $DATA_RESOURCE_GROUP --query '[].name' -o tsv) PGUSER=$(az postgres flexible-server list --resource-group $DATA_RESOURCE_GROUP --query '[].administratorLogin' -o tsv) PGPASSWORD_SECRET_NAME=${PGPASSWORD_SECRET_NAME:-"PGAdminLoginPass"} PGPASSWORD=$(az keyvault secret show --vault-name $KEY_VAULT_NAME --name $PGPASSWORD_SECRET_NAME --query value -o tsv) PGDATABASE=${PGDATABASE:-"postgres"} PGPORT=${PGPORT:-"5432"} STACIFY_STORAGE_CONTAINER_NAME=${STACIFY_STORAGE_CONTAINER_NAME:-"stacify"} STACIFY_SERVICE_BUS_TOPIC_NAME=${STACIFY_SERVICE_BUS_TOPIC_NAME:-"stacifytopic"} STACIFY_SERVICE_BUS_TOPIC_AUTH_POLICY_NAME=${STACIFY_SERVICE_BUS_TOPIC_AUTH_POLICY_NAME:-"stacifypolicy"} STACIFY_SERVICE_BUS_SUBSCRIPTION_NAME=${STACIFY_SERVICE_BUS_SUBSCRIPTION_NAME:-"stacifysubscription"} STACIFY_SERVICE_BUS_CONNECTION_STRING=$(az servicebus topic authorization-rule keys list \ --resource-group ${DATA_RESOURCE_GROUP} \ --namespace-name ${SERVICE_BUS_NAMESPACE} \ --topic ${STACIFY_SERVICE_BUS_TOPIC_NAME} \ --name ${STACIFY_SERVICE_BUS_TOPIC_AUTH_POLICY_NAME} \ --query "primaryConnectionString" -otsv) STAC_COLLECTION_IMAGE_NAME=${STAC_COLLECTION_IMAGE_NAME:-"stac-collection"} STACCOLLECTION_STORAGE_CONTAINER_NAME=${STACCOLLECTION_STORAGE_CONTAINER_NAME:-"staccollection"} STACCOLLECTION_SERVICE_BUS_TOPIC_NAME=${STACCOLLECTION_SERVICE_BUS_TOPIC_NAME:-"staccollectiontopic"} STACCOLLECTION_SERVICE_BUS_AUTH_POLICY_NAME=${STACCOLLECTION_SERVICE_BUS_AUTH_POLICY_NAME:-"staccollectionpolicy"} STACCOLLECTION_SERVICE_BUS_SUBSCRIPTION_NAME=${STACCOLLEcTION_SERVICE_BUS_SUBSCRIPTION_NAME:-"staccollectionsubscription"} STACCOLLECTION_SERVICE_BUS_CONNECTION_STRING=$(az servicebus topic authorization-rule keys list \ --resource-group ${DATA_RESOURCE_GROUP} \ --namespace-name ${SERVICE_BUS_NAMESPACE} \ --topic ${STACCOLLECTION_SERVICE_BUS_TOPIC_NAME} \ --name ${STACCOLLECTION_SERVICE_BUS_AUTH_POLICY_NAME} \ --query "primaryConnectionString" -otsv) INGRESS_PUBLIC_IP_ADDR=$(az network public-ip show -g $PROCESSING_RESOURCE_GROUP \ -n ${ENV_CODE}-stac-ingress-public-ip --query ipAddress -o tsv) INGRESS_FQDN=$(az network public-ip show -g $PROCESSING_RESOURCE_GROUP -n ${ENV_CODE}-stac-ingress-public-ip \ --query dnsSettings.fqdn -o tsv) DNS_LABEL_NAME=${INGRESS_FQDN%%.*} set +a echo 'enabling POSTGIS,BTREE_GIST in postgres' az postgres flexible-server \ parameter set \ --resource-group $DATA_RESOURCE_GROUP --server-name $PGHOSTONLY \ --subscription $SUBSCRIPTION --name azure.extensions --value POSTGIS,BTREE_GIST \ -o none az aks get-credentials --admin --resource-group ${AKS_RESOURCE_GROUP} \ --name ${AKS_CLUSTER_NAME} --context ${AKS_CLUSTER_NAME} --overwrite-existing \ -o none kubectl config set-context ${AKS_CLUSTER_NAME} NS=$(kubectl get namespace $AKS_NAMESPACE --ignore-not-found); if [[ "$NS" ]]; then echo "Skipping creation of ${AKS_NAMESPACE} namespace in k8s cluster as it already exists" else echo "Creating ${AKS_NAMESPACE} namespace in k8s cluster" kubectl create namespace ${AKS_NAMESPACE} fi; echo "Install KEDA" helm repo add kedacore https://kedacore.github.io/charts || true helm repo update [[ -z "$(kubectl get namespace keda --ignore-not-found)" ]] && kubectl create namespace keda helm upgrade --install keda kedacore/keda --namespace keda # Deploy a dummy workload to work around issue https://github.com/kedacore/keda/issues/4224 cat <<"EOF" | kubectl apply -n keda -f - apiVersion: apps/v1 kind: Deployment metadata: name: dummy-workload spec: replicas: 0 selector: matchLabels: app: dummy-workload template: metadata: labels: app: dummy-workload spec: containers: - name: dummy image: busybox --- apiVersion: keda.sh/v1alpha1 kind: ScaledObject metadata: name: dummy-workload spec: scaleTargetRef: name: dummy-workload minReplicaCount: 0 maxReplicaCount: 1 triggers: - type: cron metadata: timezone: Etc/UTC start: 30 * * * * end: 45 * * * * desiredReplicas: "0" EOF kubectl -n keda wait --for=condition=Ready ScaledObject/dummy-workload --timeout=3m sleep 10 echo "Deploying stac-scaler chart to Kubernetes Cluster" helm upgrade --install stac-scaler ${PRJ_ROOT}/deploy/helm/stac-scaler \ --namespace ${AKS_NAMESPACE} \ --create-namespace \ --set envCode=${ENV_CODE} \ --set repository=${ACR_DNS} \ --set userAssignedClientId=${USER_ASSIGNED_CLIENT_ID} \ --set serviceAccountName=${AKS_SERVICE_ACCOUNT_NAME} \ --set keyVaultName=${KEY_VAULT_NAME} \ --set tenantId=${IDENTITY_TENANT} \ --set cloudName=$(az cloud show --query name -otsv) \ --set processors.staccollection.env.DATA_STORAGE_ACCOUNT_NAME=${DATA_STORAGE_ACCOUNT_NAME} \ --set processors.staccollection.env.STACCOLLECTION_STORAGE_CONTAINER_NAME=${STACCOLLECTION_STORAGE_CONTAINER_NAME} \ --set processors.staccollection.env.PGHOST=${PGHOST} \ --set processors.staccollection.env.PGUSER=${PGUSER} \ --set processors.staccollection.env.PGDATABASE=${PGDATABASE} \ --set processors.staceventconsumer.env.DATA_STORAGE_ACCOUNT_NAME=${DATA_STORAGE_ACCOUNT_NAME} \ --set processors.staceventconsumer.env.GENERATED_STAC_STORAGE_CONTAINER_NAME=${GENERATED_STAC_STORAGE_CONTAINER_NAME} \ --set processors.staceventconsumer.env.DATA_STORAGE_PGSTAC_CONTAINER_NAME=${DATA_STORAGE_PGSTAC_CONTAINER_NAME} \ --set processors.staceventconsumer.env.PGHOST=${PGHOST} \ --set processors.staceventconsumer.env.PGUSER=${PGUSER} \ --set processors.staceventconsumer.env.PGDATABASE=${PGDATABASE} \ --set processors.generatestacjson.env.DATA_STORAGE_ACCOUNT_NAME=${DATA_STORAGE_ACCOUNT_NAME} \ --set processors.generatestacjson.env.STACIFY_STORAGE_CONTAINER_NAME=${STACIFY_STORAGE_CONTAINER_NAME} \ --set processors.generatestacjson.env.GENERATED_STAC_STORAGE_CONTAINER_NAME=${GENERATED_STAC_STORAGE_CONTAINER_NAME} \ --set processors.generatestacjson.env.DATA_STORAGE_PGSTAC_CONTAINER_NAME=${DATA_STORAGE_PGSTAC_CONTAINER_NAME} \ --set processors.generatestacjson.env.STAC_METADATA_TYPE_NAME=${STAC_METADATA_TYPE_NAME} \ --set stacfastapi.image.repository=${ACR_DNS} \ --set stacfastapi.env.POSTGRES_HOST_READER=${PGHOST} \ --set stacfastapi.env.POSTGRES_HOST_WRITER=${PGHOST} \ --set stacfastapi.env.POSTGRES_USER=${PGUSER} \ --set stacfastapi.env.PGUSER=${PGUSER} \ --set stacfastapi.env.PGHOST=${PGHOST} \ --set stacfastapi.blobStoreEndpoint=${DATA_STORAGE_ACCOUNT_BLOB_ENDPOINT} \ --set stacfastapi.hostname=${INGRESS_FQDN} \ --set stacfastapi.clusterIssuer=letsencrypt echo "Deploying stac-browser chart to Kubernetes Cluster" helm upgrade --install stac-browser ${PRJ_ROOT}/deploy/helm/stac-browser \ --namespace ${AKS_NAMESPACE} \ --create-namespace \ --set envCode=${ENV_CODE} \ --set repository=${ACR_DNS} \ --set hostname=${INGRESS_FQDN} \ --set clusterIssuer=letsencrypt # Add helm repos for the nginx ingress controller and cert-manager helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx helm repo add jetstack https://charts.jetstack.io helm repo update # Install the cert-manager Helm chart (for letsencrypt certificates) echo "Installing cert-manager" kubectl create namespace $AKS_INGRESS_NAMESPACE kubectl label namespace $AKS_INGRESS_NAMESPACE cert-manager.io/disable-validation=true helm upgrade --install cert-manager jetstack/cert-manager \ --create-namespace \ --namespace $AKS_INGRESS_NAMESPACE \ --set installCRDs=true \ --set nodeSelector."kubernetes\.io/os"=linux # Create a ClusterIssuer to issue certificates from Lets Encrypt cat <