EXIP base infrastructure build from UK-Export-Finance/exip #95
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This code snippet is a YAML configuration file for a GitHub Actions workflow. It defines a series of jobs and steps to set up infrastructure, create base | |
# infrastructure resources, configure security settings, configure a web app, enable logging and diagnostic settings, and set up health check alerts. | |
# The workflow is triggered on pushes to the 'infrastructure' and 'EMS-2898-database-tier' branches. It uses environment variables to define the product, environment, | |
# timezone, and target. The jobs are organized into sections for setup, base infrastructure creation, security configuration, web app configuration, logging and diagnostic settings, and health check alerts. | |
# Each step in the workflow performs a specific task using the Azure CLI, such as creating resource groups, app service plans, log analytics workspaces, container registries, | |
# virtual networks, subnets, VNET peering, databases, web apps, front doors, WAF policies, private endpoints, DNS configurations, and more. | |
# The workflow also includes conditional steps based on the target environment, such as different configurations for pre-production and production environments. | |
# Overall, this code snippet provides a comprehensive configuration for setting up infrastructure and configuring various resources in an Azure environment using GitHub Actions. | |
# | |
# Inputs | |
# ------ | |
# The inputs for this code snippet include the environment, target, and various variables such as region, database SKU, database tier, app service plan SKU, log quota, log retention day, log plan, | |
# VNET address prefix, VNET subnet database prefix, VNET subnet web app prefix, VNET subnet private prefix, health probe path, health probe protocol, health probe HTTP method, health probe latency, | |
# load balancing sample size, load balancing samples, load balancing interval, TLS version, web app log level, web app log destination, action group email, and various secrets. | |
# | |
# Flow | |
# ---- | |
# The code starts by setting up infrastructure variables and outputs the environment and timezone. | |
# It then creates the base infrastructure, including the resource group, app service plan, log analytics workspace, container registry, virtual network, | |
# subnets, VNET peering, database, web app, and front door. | |
# Next, it sets up security measures such as private endpoints, private DNS, and WAF policies. | |
# It then configures the web app, including enabling continuous deployment, setting configuration variables, and creating a database connection. | |
# The code sets up diagnostic settings for logging and monitoring of various resources. | |
# Finally, it creates an alert for health checks on the front door. | |
# | |
# Outputs | |
# ------- | |
# The outputs of this code snippet include the environment and various resources created, such as the resource group, app service plan, log analytics workspace, container | |
# registry, virtual network, subnets, database, web app, front door, and security policies. | |
# | |
name: Infrastructure π¨ | |
run-name: EXIP base infrastructure build from ${{ github.repository }} | |
on: | |
push: | |
branches: | |
- infrastructure | |
# TODO: Remove below prior to merge | |
- feat/EMS-2186-ia-c-key-vault-cost-alerts | |
env: | |
PRODUCT: exip | |
ENVIRONMENT: infrastructure | |
TIMEZONE: '${{ vars.TIMEZONE }}' | |
# Deployment environment target i.e., `development`, `staging`, `production` | |
TARGET: ${{ vars.ENVIRONMENT }} | |
jobs: | |
# 1. Setup infrastructure variables | |
setup: | |
name: Setup π§ | |
runs-on: [self-hosted, EXIP, infrastructure] | |
outputs: | |
environment: ${{ env.ENVIRONMENT }} | |
timezone: ${{ env.TIMEZONE }} | |
steps: | |
- name: Environment π§ͺ | |
run: echo "Environment set to ${{ env.ENVIRONMENT }}" | |
- name: Timezone π | |
run: echo "Timezone set to ${{ env.TIMEZONE }}" | |
# 2. Base infrastructure creation | |
base: | |
name: Base 𧱠| |
needs: setup | |
environment: ${{ needs.setup.outputs.environment }} | |
outputs: | |
environment: ${{ env.ENVIRONMENT }} | |
runs-on: [self-hosted, EXIP, infrastructure] | |
steps: | |
- name: Pre-production π« | |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
run: | | |
echo "TYPE=Preproduction" >> $GITHUB_ENV | |
echo "DB_SKU=${{ VARS.DB_NON_PROD_SKU }}" >> $GITHUB_ENV | |
echo "DB_TIER=${{ VARS.DB_NON_PROD_TIER }}" >> $GITHUB_ENV | |
- name: Production π« | |
if: ${{ 'production' == env.TARGET }} | |
run: | | |
echo "TYPE=Production" >> $GITHUB_ENV | |
echo "DB_SKU=${{ VARS.DB_PROD_SKU }}" >> $GITHUB_ENV | |
echo "DB_TIER=${{ VARS.DB_PROD_TIER }}" >> $GITHUB_ENV | |
- name: Tags π·οΈ | |
run: echo TAGS='Environment=${{ env.TYPE }}' \ | |
'Product=${{ env.PRODUCT }}' \ | |
'Team=development' >> $GITHUB_ENV | |
- name: Login π | |
uses: azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Azure defaults β¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Basic | |
az configure --defaults location=${{ vars.REGION }} | |
az configure --defaults group=rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
- name: Resource group ποΈ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az group create \ | |
--name rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--tags ${{ env.TAGS }} | |
- name: Plan π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az appservice plan create \ | |
--name appservice-plan-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--is-linux \ | |
--sku ${{ vars.APP_SERVICE_PLAN }} \ | |
--tags ${{ env.TAGS }} | |
- name: Log analytics workspace π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az monitor log-analytics workspace create \ | |
--name log-workspace-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--quota ${{ vars.LOG_QUOTA }} \ | |
--retention-time ${{ vars.LOG_RETENTION_DAY }} \ | |
--sku ${{ vars.LOG_PLAN }} \ | |
--tags ${{ env.TAGS }} | |
- name: Container registry π¦οΈ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az acr create \ | |
--name cr${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} \ | |
--sku Standard \ | |
--admin-enabled true \ | |
--workspace log-workspace-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--tags ${{ env.TAGS }} | |
- name: Virtual network 𧡠| |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az network vnet create \ | |
--name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--address-prefix ${{ vars.VNET_ADDRESS_PREFIX }} \ | |
--tags ${{ env.TAGS }} | |
- name: Subnets π« | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# WebApp | |
az network vnet subnet create \ | |
--name snet-webapp-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--address-prefixes ${{ vars.VNET_SUBNET_WEBAPP_PREFIX }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
# Private endpoint | |
az network vnet subnet create \ | |
--name snet-private-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--address-prefixes ${{ vars.VNET_SUBNET_PRIVATE_PREFIX }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
az network vnet subnet create \ | |
--name snet-keyvault-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--address-prefixes ${{ vars.VNET_SUBNET_KEYVAULT_PREFIX }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--service-endpoints Microsoft.KeyVault | |
- name: VNET Peer - AMI π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Azure Managed Instance (AMI) SQL DB VNET peering | |
# Local VNET peer | |
az network vnet peering create \ | |
--name vnet-peer-ami-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--remote-vnet $(az network vnet show --subscription ${{ secrets.REMOTE_VNET_SUBSCRIPTION_AMI }} --resource-group ${{ secrets.REMOTE_VNET_RESOURCE_GROUP_AMI }} --name ${{ secrets.REMOTE_VNET_NAME_AMI }} --query 'id' -o tsv) \ | |
--allow-vnet-access 1 | |
# Remote VNET peer | |
az network vnet peering create \ | |
--name vnet-peer-ami-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name ${{ secrets.REMOTE_VNET_NAME_AMI }} \ | |
--remote-vnet $(az network vnet show --name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query 'id' -o tsv) \ | |
--allow-vnet-access 1 \ | |
--subscription ${{ secrets.REMOTE_VNET_SUBSCRIPTION_AMI }} \ | |
--resource-group ${{ secrets.REMOTE_VNET_RESOURCE_GROUP_AMI }} | |
# Fetch peering state | |
echo "Peering state: $(az network vnet peering show \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--name vnet-peer-ami-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--query peeringState)" | |
- name: VNET Peer - APIM π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# API management (APIM) VNET peering | |
# Local VNET peer | |
az network vnet peering create \ | |
--name vnet-peer-apim-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--remote-vnet $(az network vnet show --subscription ${{ secrets.REMOTE_VNET_SUBSCRIPTION_APIM }} --resource-group ${{ secrets.REMOTE_VNET_RESOURCE_GROUP_APIM }} --name ${{ secrets.REMOTE_VNET_NAME_APIM }} --query 'id' -o tsv) \ | |
--allow-vnet-access 1 | |
# Remote VNET peer | |
az network vnet peering create \ | |
--name vnet-peer-apim-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name ${{ secrets.REMOTE_VNET_NAME_APIM }} \ | |
--remote-vnet $(az network vnet show --name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query 'id' -o tsv) \ | |
--allow-vnet-access 1 \ | |
--subscription ${{ secrets.REMOTE_VNET_SUBSCRIPTION_APIM }} \ | |
--resource-group ${{ secrets.REMOTE_VNET_RESOURCE_GROUP_APIM }} | |
# Fetch peering state | |
echo "Peering state: $(az network vnet peering show \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--name vnet-peer-apim-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--query peeringState)" | |
- name: VNET Peer - UKS π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UKS VNET peering | |
# Local VNET peer | |
az network vnet peering create \ | |
--name vnet-peer-uks-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--remote-vnet $(az network vnet show --subscription ${{ secrets.REMOTE_VNET_SUBSCRIPTION_UKS }} --resource-group ${{ secrets.REMOTE_VNET_RESOURCE_GROUP_UKS }} --name ${{ secrets.REMOTE_VNET_NAME_UKS }} --query 'id' -o tsv) \ | |
--allow-vnet-access 1 | |
# Remote VNET peer | |
az network vnet peering create \ | |
--name vnet-peer-uks-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name ${{ secrets.REMOTE_VNET_NAME_UKS }} \ | |
--remote-vnet $(az network vnet show --name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query 'id' -o tsv) \ | |
--allow-vnet-access 1 \ | |
--subscription ${{ secrets.REMOTE_VNET_SUBSCRIPTION_UKS }} \ | |
--resource-group ${{ secrets.REMOTE_VNET_RESOURCE_GROUP_UKS }} | |
# Fetch peering state | |
echo "Peering state: $(az network vnet peering show \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--name vnet-peer-uks-${{ env.TARGET }}-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--query peeringState)" | |
- name: Database πΎ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az mysql flexible-server create \ | |
--name sqldb-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--admin-user ${{ secrets.MYSQL_USER }} \ | |
--admin-password ${{ secrets.MYSQL_PASSWORD }} \ | |
--database-name ${{ env.PRODUCT }} \ | |
--storage-size ${{ vars.DB_STORAGE_SIZE_GB }} \ | |
--sku-name ${{ env.DB_SKU }} \ | |
--tier ${{ env.DB_TIER }} \ | |
--version ${{ vars.DB_VERSION }} \ | |
--vnet vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--subnet snet-database-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--address-prefixes ${{ vars.VNET_ADDRESS_PREFIX }} \ | |
--subnet-prefix ${{ vars.VNET_SUBNET_DATABASE_PREFIX }} \ | |
--backup-retention ${{ vars.DB_DR_DAYS }} \ | |
--auto-scale-iops Enabled \ | |
--storage-auto-grow Enabled \ | |
--yes \ | |
--tags ${{ env.TAGS }} | |
- name: Web app - UI π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az webapp create \ | |
--name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--plan appservice-plan-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--deployment-container-image-name cr${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }}.azurecr.io/ui:${{ env.TARGET }} \ | |
--vnet vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--subnet snet-webapp-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--https-only true \ | |
--tags ${{ env.TAGS }} | |
- name: Web app - API π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az webapp create \ | |
--name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--plan appservice-plan-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--deployment-container-image-name cr${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }}.azurecr.io/api:${{ env.TARGET }} \ | |
--vnet vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--subnet snet-webapp-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--https-only true \ | |
--tags ${{ env.TAGS }} | |
- name: Front Door πͺ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Profile | |
az afd profile create \ | |
--profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--sku ${{ secrets.AZURE_FRONT_DOOR_SKU }} \ | |
--tags ${{ env.TAGS }} | |
# Endpoint | |
az afd endpoint create \ | |
--endpoint-name ${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--tags ${{ env.TAGS }} | |
# Origin Group | |
az afd origin-group create \ | |
--origin-group-name ui \ | |
--probe-path ${{ vars.HEALTH_PROBE_PATH }} \ | |
--probe-protocol ${{ vars.HEALTH_PROBE_PROTOCOL }} \ | |
--probe-request-type ${{ vars.HEALTH_PROBE_HTTP_METHOD }} \ | |
--additional-latency-in-milliseconds ${{ vars.HEALTH_PROBE_LATENCY }} \ | |
--sample-size ${{ vars.LOAD_BALANCING_SAMPLE_SIZE }} \ | |
--successful-samples-required ${{ vars.LOAD_BALANCING_SAMPLES }} \ | |
--probe-interval-in-seconds ${{ vars.LOAD_BALANCING_INTERVAL }} \ | |
--profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
# 3. Security | |
security: | |
name: Security π | |
needs: base | |
environment: ${{ needs.base.outputs.environment }} | |
runs-on: [self-hosted, EXIP, infrastructure] | |
steps: | |
- name: Pre-production π« | |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
run: echo "TYPE=Preproduction" >> $GITHUB_ENV | |
- name: Production π« | |
if: ${{ 'production' == env.TARGET }} | |
run: echo "TYPE=Production" >> $GITHUB_ENV | |
- name: Tags π·οΈ | |
run: echo TAGS='Environment=${{ env.TYPE }}' \ | |
'Product=${{ env.PRODUCT }}' \ | |
'Team=development' >> $GITHUB_ENV | |
- name: Login π | |
uses: azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Azure defaults β¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Basic | |
az configure --defaults location=${{ vars.REGION }} | |
az configure --defaults group=rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
- name: Extension β | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az extension add --name front-door | |
- name: Key Vault π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az keyvault create \ | |
--name kv-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--default-action Deny \ | |
--enable-purge-protection true \ | |
--public-network-access Disabled \ | |
--network-acls-ips ${{ secrets.WAF_ALLOWED_IP }} \ | |
--network-acls-vnets $(az network vnet subnet list --vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query '[?contains(name, `keyvault`)].id' -o tsv) | |
- name: Private endpoint π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
#UI | |
az network private-endpoint create \ | |
--name private-endpoint-ui-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--private-connection-resource-id $(az webapp list --query '[?contains(name, `app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }}`)].id' -o tsv) \ | |
--connection-name private-link-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--subnet snet-private-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--group-id sites \ | |
--tags ${{ env.TAGS }} | |
#API | |
az network private-endpoint create \ | |
--name private-endpoint-api-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--private-connection-resource-id $(az webapp list --query '[?contains(name, `app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }}`)].id' -o tsv) \ | |
--connection-name private-link-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--subnet snet-private-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--group-id sites \ | |
--tags ${{ env.TAGS }} | |
#Key Vault | |
az network private-endpoint create \ | |
--name private-endpoint-keyvault-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--private-connection-resource-id $(az keyvault show --name kv-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query id -o tsv) \ | |
--connection-name private-link-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--subnet snet-private-${{ env.PRODUCT }}-${{ vars.VERSION }} \ | |
--vnet-name vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--group-id vault \ | |
--tags ${{ env.TAGS }} | |
- name: Private DNS π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az network private-dns zone create \ | |
--name ${{ secrets.PRIVATE_DNS_ZONE_NAME }} \ | |
--tags ${{ env.TAGS }} | |
- name: DNS VNET β | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az network private-dns link vnet create \ | |
--zone-name ${{ secrets.PRIVATE_DNS_ZONE_NAME }} \ | |
--name dns-vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--virtual-network vnet-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--registration-enabled false \ | |
--tags ${{ env.TAGS }} | |
- name: DNS Records π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
#UI | |
az network private-endpoint dns-zone-group create \ | |
--endpoint-name private-endpoint-ui-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--name ${{ env.PRODUCT }} \ | |
--private-dns-zone ${{ secrets.PRIVATE_DNS_ZONE_NAME }} \ | |
--zone-name webapp | |
#API | |
az network private-endpoint dns-zone-group create \ | |
--endpoint-name private-endpoint-api-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--name ${{ env.PRODUCT }} \ | |
--private-dns-zone ${{ secrets.PRIVATE_DNS_ZONE_NAME }} \ | |
--zone-name webapp | |
- name: Front Door πͺ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Origin | |
az afd origin create \ | |
--origin-name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--host-name $(az webapp show --name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} --query defaultHostName -o tsv) \ | |
--origin-host-header $(az webapp show --name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} --query defaultHostName -o tsv) \ | |
--priority 1 \ | |
--weight 1000 \ | |
--enable-private-link true \ | |
--private-link-location ${{ vars.REGION }} \ | |
--private-link-resource $(az webapp show --name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} --query id -o tsv) \ | |
--private-link-request-message '${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }}' \ | |
--private-link-sub-resource-type $(az network private-link-resource list --id $(az webapp show --name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} --query id -o tsv) --query [].name -o tsv) \ | |
--origin-group-name ui \ | |
--profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--enabled-state Enabled | |
# Route | |
az afd route create \ | |
--route-name default \ | |
--forwarding-protocol HttpsOnly \ | |
--https-redirect Enabled \ | |
--supported-protocols Http Https \ | |
--enable-caching false \ | |
--link-to-default-domain Enabled \ | |
--origin-path / \ | |
--origin-group ui \ | |
--endpoint-name ${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--enabled-state Enabled | |
- name: WAF π₯ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# WAF | |
az network front-door waf-policy create \ | |
--name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} \ | |
--custom-block-response-body "${{ vars.WAF_BLOCK_MESSAGE }}" \ | |
--custom-block-response-status-code ${{ vars.WAF_BLOCK_CODE }} \ | |
--redirect-url ${{ vars.WAF_REDIRECT_URL }} \ | |
--mode ${{ vars.WAF_MODE }} \ | |
--sku ${{ secrets.AZURE_FRONT_DOOR_SKU }} \ | |
--tags ${{ env.TAGS }} | |
# Managed rule - DR | |
az network front-door waf-policy managed-rules add \ | |
--version 2.1 \ | |
--type Microsoft_DefaultRuleSet \ | |
--action Block \ | |
--policy-name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} | |
# Managed rule - Bot | |
az network front-door waf-policy managed-rules add \ | |
--version 1.0 \ | |
--type Microsoft_BotManagerRuleSet \ | |
--action Block \ | |
--policy-name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} | |
# Associate FD | |
az afd security-policy create \ | |
--security-policy-name security-policy-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--waf-policy $(az network front-door waf-policy list --query [].id -o tsv) \ | |
--domains $(az afd endpoint list --profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query [].id -o tsv) \ | |
--profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
# Custom rule - RL | |
az network front-door waf-policy rule create \ | |
--action Block \ | |
--name HTTPRateLimitRule \ | |
--policy-name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} \ | |
--priority 2 \ | |
--rule-type RateLimitRule \ | |
--rate-limit-duration ${{ vars.WAF_RL_DURATION }} \ | |
--rate-limit-threshold ${{ vars.WAF_RL_THRESHOLD }} \ | |
--defer | |
# Custom rule - RL condition | |
az network front-door waf-policy rule match-condition add \ | |
--match-variable ${{ vars.WAF_RL_MATCH }} \ | |
--operator GreaterThanOrEqual \ | |
--values ${{ vars.WAF_RL_MATCH_VALUE }} \ | |
--name HTTPRateLimitRule \ | |
--policy-name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} | |
- name: VPN 𧱠| |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Custom rule - IP | |
az network front-door waf-policy rule create \ | |
--action Redirect \ | |
--name IPAllowListRule \ | |
--policy-name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} \ | |
--priority 1 \ | |
--rule-type MatchRule \ | |
--defer | |
# Custom rule - IP condition | |
az network front-door waf-policy rule match-condition add \ | |
--match-variable ${{ vars.WAF_IP_ADDRESS }} \ | |
--operator IPMatch \ | |
--values ${{ secrets.WAF_ALLOWED_IP }} \ | |
--negate \ | |
--name IPAllowListRule \ | |
--policy-name waf${{ env.PRODUCT }}${{ env.TARGET }}${{ vars.VERSION }} | |
# 4. WebApp configuration | |
webapp: | |
name: Web App π§ | |
needs: [base, security] | |
environment: ${{ needs.base.outputs.environment }} | |
runs-on: [self-hosted, EXIP, infrastructure] | |
steps: | |
- name: Pre-production π« | |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
run: echo "TYPE=Preproduction" >> $GITHUB_ENV | |
- name: Production π« | |
if: ${{ 'production' == env.TARGET }} | |
run: echo "TYPE=Production" >> $GITHUB_ENV | |
- name: Login π | |
uses: azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Azure defaults β¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Basic | |
az configure --defaults location=${{ vars.REGION }} | |
az configure --defaults group=rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
- name: CD π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UI | |
az webapp deployment container config \ | |
--name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--enable-cd true | |
# API | |
az webapp deployment container config \ | |
--name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--enable-cd true | |
- name: Configuration π¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UI | |
az webapp config set \ | |
--name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--always-on true \ | |
--ftps-state Disabled \ | |
--http20-enabled true \ | |
--min-tls-version '${{ vars.TLS_VERSION }}' | |
# API | |
az webapp config set \ | |
--name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--always-on true \ | |
--ftps-state Disabled \ | |
--http20-enabled true \ | |
--min-tls-version '${{ vars.TLS_VERSION }}' | |
- name: Variables β¨ | |
run: | | |
echo "API_URL=$(az webapp show --name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} --query defaultHostName -o tsv)" >> $GITHUB_ENV | |
- name: Setting 𧱠| |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UI | |
az webapp config appsettings set \ | |
--name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--settings \ | |
TZ='${{ vars.TIMEZONE }}' \ | |
NODE_ENV='${{ vars.NODE_ENV }}' \ | |
PORT='${{ vars.UI_PORT }}' \ | |
WEBSITES_PORT='${{ vars.UI_PORT }}' \ | |
TLS_CERTIFICATE='${{ secrets.TLS_CERTIFICATE }}' \ | |
TLS_KEY='${{ secrets.TLS_KEY }}' \ | |
SESSION_SECRET='${{ secrets.SESSION_SECRET }}' \ | |
GOOGLE_ANALYTICS_ID='${{ secrets.GOOGLE_ANALYTICS_ID }}' \ | |
GOOGLE_TAG_MANAGER_ID='${{ secrets.GOOGLE_TAG_MANAGER_ID }}' \ | |
API_URL='https://${{ env.API_URL }}/api/graphql' \ | |
API_KEY='${{ secrets.API_KEY }}' \ | |
APIM_MDM_URL='${{ secrets.APIM_MDM_URL }}' \ | |
APIM_MDM_KEY='${{ secrets.APIM_MDM_KEY }}' \ | |
APIM_MDM_VALUE='${{ secrets.APIM_MDM_VALUE }}' | |
# API | |
az webapp config appsettings set \ | |
--name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--settings \ | |
TZ='${{ vars.TIMEZONE }}' \ | |
NODE_ENV='${{ vars.NODE_ENV }}' \ | |
PORT='${{ vars.API_PORT }}' \ | |
WEBSITES_PORT='${{ vars.API_PORT }}' \ | |
TLS_CERTIFICATE='${{ secrets.TLS_CERTIFICATE }}' \ | |
TLS_KEY='${{ secrets.TLS_KEY }}' \ | |
DATABASE_URL='${{ secrets.DATABASE_URL }}' \ | |
MYSQL_DATABASE='${{ secrets.MYSQL_DATABASE }}' \ | |
MYSQL_ROOT_PASSWORD='${{ secrets.MYSQL_ROOT_PASSWORD }}' \ | |
API_KEY='${{ secrets.API_KEY }}' \ | |
APIM_MDM_URL='${{ secrets.APIM_MDM_URL }}' \ | |
APIM_MDM_KEY='${{ secrets.APIM_MDM_KEY }}' \ | |
APIM_MDM_VALUE='${{ secrets.APIM_MDM_VALUE }}' \ | |
SESSION_SECRET='${{ secrets.SESSION_SECRET }}' \ | |
GOV_NOTIFY_API_KEY='${{ secrets.GOV_NOTIFY_API_KEY }}' \ | |
COMPANIES_HOUSE_API_URL='${{ secrets.COMPANIES_HOUSE_API_URL }}' \ | |
COMPANIES_HOUSE_API_KEY='${{ secrets.COMPANIES_HOUSE_API_KEY }}' \ | |
JWT_SIGNING_KEY='${{ secrets.JWT_SIGNING_KEY }}' \ | |
UNDERWRITING_TEAM_EMAIL='${{ secrets.UNDERWRITING_TEAM_EMAIL }}' \ | |
FEEDBACK_EMAIL_RECIPIENT='${{ secrets.FEEDBACK_EMAIL_RECIPIENT }}' | |
CRON_SCHEDULE_UNVERIFIED_ACCOUNT='${{ secrets.CRON_SCHEDULE_UNVERIFIED_ACCOUNT }}' | |
CRON_SCHEDULE_INACTIVE_APPLICATION='${{ secrets.CRON_SCHEDULE_INACTIVE_APPLICATION }}' | |
CRON_SCHEDULE_SUBMISSION_DEADLINE_REMINDER_EMAIL='${{ secrets.CRON_SCHEDULE_SUBMISSION_DEADLINE_REMINDER_EMAIL }}' | |
APPLICATION_URL='${{ secrets.APPLICATION_URL }}' | |
- name: Extension β | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az config set extension.use_dynamic_install=yes_without_prompt | |
- name: Repository ποΈ | |
uses: actions/checkout@v4 | |
- name: Database πΎ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az webapp connection create mysql-flexible \ | |
--source-id $(az webapp show --name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} --query id -o tsv) \ | |
--target-id $(az mysql flexible-server list --query [].id -o tsv)/databases/${{ env.PRODUCT }} \ | |
--connection webapp_api_mysqlflexible_${{ env.PRODUCT }}_${{ env.TARGET }}_${{ vars.VERSION }} \ | |
--secret name=${{ secrets.MYSQL_USER }} secret=${{ secrets.MYSQL_PASSWORD }} \ | |
--client-type nodejs | |
# 5. Public DNS Zone | |
dns: | |
name: DNS π | |
needs: [base, security, webapp] | |
environment: ${{ needs.base.outputs.environment }} | |
runs-on: [self-hosted, EXIP, infrastructure] | |
steps: | |
- name: Pre-production π« | |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
run: echo "TYPE=Preproduction" >> $GITHUB_ENV | |
- name: Production π« | |
if: ${{ 'production' == env.TARGET }} | |
run: echo "TYPE=Production" >> $GITHUB_ENV | |
- name: Tags π·οΈ | |
run: echo TAGS='Environment=${{ env.TYPE }}' \ | |
'Product=${{ env.PRODUCT }}' \ | |
'Team=development' >> $GITHUB_ENV | |
- name: Login π | |
uses: azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Azure defaults β¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Basic | |
az configure --defaults location=${{ vars.REGION }} | |
az configure --defaults group=rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
# AFD | |
echo FRONTDOOR=$(az afd profile list --query '[].id' -o tsv) >> $GITHUB_ENV | |
echo ENDPOINT=$(az afd endpoint list --profile-name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} --query '[].name' -o tsv) >> $GITHUB_ENV | |
- name: DNS Zones π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az network dns zone create \ | |
--name ${{ vars.DOMAIN_QUOTE }} \ | |
--tags ${{ env.TAGS }} | |
az network dns zone create \ | |
--name ${{ vars.DOMAIN_INSURANCE }} \ | |
--tags ${{ env.TAGS }} | |
- name: A records | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az network dns record-set a create \ | |
--name "@" \ | |
--zone ${{ vars.DOMAIN_QUOTE }} \ | |
--if-none-match "*" \ | |
--target-resource ${{ env.FRONTDOOR }}/afdendpoints/${{ env.ENDPOINT }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
az network dns record-set a create \ | |
--name "@" \ | |
--zone ${{ vars.DOMAIN_INSURANCE }} \ | |
--if-none-match "*" \ | |
--target-resource ${{ env.FRONTDOOR }}/afdendpoints/${{ env.ENDPOINT }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
- name: CNAME records | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az network dns record-set cname create \ | |
--name "*" \ | |
--zone ${{ vars.DOMAIN_QUOTE }} \ | |
--if-none-match "*" \ | |
--target-resource ${{ env.FRONTDOOR }}/afdendpoints/${{ env.ENDPOINT }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
az network dns record-set cname create \ | |
--name "*" \ | |
--zone ${{ vars.DOMAIN_INSURANCE }} \ | |
--if-none-match "*" \ | |
--target-resource ${{ env.FRONTDOOR }}/afdendpoints/${{ env.ENDPOINT }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
- name: TXT records | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# CA Verification | |
az network dns record-set txt add-record \ | |
--record-set-name "@" \ | |
--zone ${{ vars.DOMAIN_QUOTE }} \ | |
--value ${{ vars.CA_VERIFICATION }} \ | |
--if-none-match "*" | |
az network dns record-set txt add-record \ | |
--record-set-name "@" \ | |
--zone ${{ vars.DOMAIN_INSURANCE }} \ | |
--value ${{ vars.CA_VERIFICATION }} \ | |
--if-none-match "*" | |
- name: CAA records | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# CREATE | |
az network dns record-set caa create \ | |
--name "@" \ | |
--zone ${{ vars.DOMAIN_QUOTE }} \ | |
--if-none-match "*" \ | |
--ttl ${{ vars.DNS_TTL }} | |
# ISSUE | |
az network dns record-set caa add-record \ | |
--record-set-name "@" \ | |
--flags ${{ vars.CAA_FLAG }} \ | |
--tag "issue" \ | |
--value ${{ vars.CAA_ISSUE_VALUE }} \ | |
--zone ${{ vars.DOMAIN_QUOTE }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
# IODEF | |
az network dns record-set caa add-record \ | |
--record-set-name "@" \ | |
--flags ${{ vars.CAA_FLAG }} \ | |
--tag "iodef" \ | |
--value ${{ vars.CAA_IODEF_VALUE }} \ | |
--zone ${{ vars.DOMAIN_QUOTE }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
# CREATE | |
az network dns record-set caa create \ | |
--name "@" \ | |
--zone ${{ vars.DOMAIN_INSURANCE }} \ | |
--if-none-match "*" \ | |
--ttl ${{ vars.DNS_TTL }} | |
# ISSUE | |
az network dns record-set caa add-record \ | |
--record-set-name "@" \ | |
--flags ${{ vars.CAA_FLAG }} \ | |
--tag "issue" \ | |
--value ${{ vars.CAA_ISSUE_VALUE }} \ | |
--zone ${{ vars.DOMAIN_INSURANCE }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
# IODEF | |
az network dns record-set caa add-record \ | |
--record-set-name "@" \ | |
--flags ${{ vars.CAA_FLAG }} \ | |
--tag "iodef" \ | |
--value ${{ vars.CAA_IODEF_VALUE }} \ | |
--zone ${{ vars.DOMAIN_INSURANCE }} \ | |
--ttl ${{ vars.DNS_TTL }} | |
# 6. Logs and dignostic settings | |
log: | |
name: Log π | |
needs: [base, security, webapp, dns] | |
environment: ${{ needs.base.outputs.environment }} | |
runs-on: [self-hosted, EXIP, infrastructure] | |
steps: | |
- name: Pre-production π« | |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
run: echo "TYPE=Preproduction" >> $GITHUB_ENV | |
- name: Production π« | |
if: ${{ 'production' == env.TARGET }} | |
run: echo "TYPE=Production" >> $GITHUB_ENV | |
- name: Login π | |
uses: azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Azure defaults β¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Basic | |
az configure --defaults location=${{ vars.REGION }} | |
az configure --defaults group=rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
- name: Log analytics workspace π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az monitor diagnostic-settings create \ | |
--name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--resource $(az monitor log-analytics workspace list --query [].id -o tsv) \ | |
--workspace $(az monitor log-analytics workspace list --query [].id -o tsv) \ | |
--logs "[{categoryGroup:allLogs,enabled:true}]" \ | |
--metrics "[{category:allMetrics,enabled:true}]" | |
- name: Database πΎ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UI | |
az webapp log config \ | |
--name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--detailed-error-messages true \ | |
--failed-request-tracing true \ | |
--level ${{ vars.WEBAPP_LOG_LEVEL }} \ | |
--application-logging ${{ vars.WEBAPP_LOG_DESTINATION }} \ | |
--docker-container-logging ${{ vars.WEBAPP_LOG_DESTINATION }} \ | |
--web-server-logging ${{ vars.WEBAPP_LOG_DESTINATION }} | |
- name: WebApp π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UI | |
az webapp log config \ | |
--name app-${{ env.PRODUCT }}-ui-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--detailed-error-messages true \ | |
--failed-request-tracing true \ | |
--level ${{ vars.WEBAPP_LOG_LEVEL }} \ | |
--application-logging ${{ vars.WEBAPP_LOG_DESTINATION }} \ | |
--docker-container-logging ${{ vars.WEBAPP_LOG_DESTINATION }} \ | |
--web-server-logging ${{ vars.WEBAPP_LOG_DESTINATION }} | |
# API | |
az webapp log config \ | |
--name app-${{ env.PRODUCT }}-api-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--detailed-error-messages true \ | |
--failed-request-tracing true \ | |
--level ${{ vars.WEBAPP_LOG_LEVEL }} \ | |
--application-logging ${{ vars.WEBAPP_LOG_DESTINATION }} \ | |
--docker-container-logging ${{ vars.WEBAPP_LOG_DESTINATION }} \ | |
--web-server-logging ${{ vars.WEBAPP_LOG_DESTINATION }} | |
- name: Front Door πͺ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az monitor diagnostic-settings create \ | |
--name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--resource $(az afd profile list --query [].id -o tsv) \ | |
--workspace $(az monitor log-analytics workspace list --query [].id -o tsv) \ | |
--logs "[{categoryGroup:allLogs,enabled:true}]" \ | |
--metrics "[{category:allMetrics,enabled:true}]" | |
- name: Private links π | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# UI | |
az monitor diagnostic-settings create \ | |
--name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--resource $(az network nic list --query ['?contains(name, `ui`)'].id -o tsv) \ | |
--workspace $(az monitor log-analytics workspace list --query [].id -o tsv) \ | |
--metrics "[{category:allMetrics,enabled:true}]" | |
# API | |
az monitor diagnostic-settings create \ | |
--name frontdoor-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--resource $(az network nic list --query ['?contains(name, `api`)'].id -o tsv) \ | |
--workspace $(az monitor log-analytics workspace list --query [].id -o tsv) \ | |
--metrics "[{category:allMetrics,enabled:true}]" | |
# 7. Various alerts | |
alert: | |
name: Alert π’ | |
needs: [base, security, webapp, dns] | |
environment: ${{ needs.base.outputs.environment }} | |
runs-on: [self-hosted, EXIP, infrastructure] | |
steps: | |
- name: Pre-production π« | |
if: contains('["dev", "feature", "staging"]', env.TARGET) | |
run: echo "TYPE=Preproduction" >> $GITHUB_ENV | |
- name: Production π« | |
if: ${{ 'production' == env.TARGET }} | |
run: echo "TYPE=Production" >> $GITHUB_ENV | |
- name: Tags π·οΈ | |
run: echo TAGS='Environment=${{ env.TYPE }}' \ | |
'Product=${{ env.PRODUCT }}' \ | |
'Team=development' >> $GITHUB_ENV | |
- name: Login π | |
uses: azure/login@v2 | |
with: | |
creds: ${{ secrets.AZURE_CREDENTIALS }} | |
- name: Azure defaults β¨ | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Basic | |
az configure --defaults location=${{ vars.REGION }} | |
az configure --defaults group=rg-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} | |
- name: Action group π© | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
az monitor action-group create \ | |
--name action-group-email-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--short-name Email \ | |
--action email ${{ secrets.ACTION_GROUP_EMAIL }} \ | |
--location global \ | |
--tags ${{ env.TAGS }} | |
- name: Monitor alert π© | |
uses: azure/cli@v2 | |
with: | |
inlineScript: | | |
# Dimension | |
az monitor metrics alert dimension create \ | |
--name "OriginGroup" \ | |
--value "ui" \ | |
--operator Include | |
# Condition | |
az monitor metrics alert condition create \ | |
--type static \ | |
--dimension "_where_ OriginGroup includes ui" \ | |
--aggregation Average \ | |
--metric OriginHealthPercentage \ | |
--operator LessThanOrEqual \ | |
--threshold 99 \ | |
--num-periods 1 \ | |
--num-violations 5 \ | |
--sensitivity High | |
# Alert | |
az monitor metrics alert create \ | |
--name alert-healthcheck-${{ env.PRODUCT }}-${{ env.TARGET }}-${{ vars.VERSION }} \ | |
--description "1 minute health check" \ | |
--condition "avg 'OriginHealthPercentage' <= 99.0 where OriginGroup includes ui" \ | |
--scope $(az afd profile list --query [].id -o tsv) \ | |
--action $(az monitor action-group list --query [].id -o tsv) \ | |
--auto-mitigate true \ | |
--severity 0 \ | |
--tags ${{ env.TAGS }} |