From 5b855018de94c6b26fffd862b8e3f15681f8dab6 Mon Sep 17 00:00:00 2001 From: Mike Monteith Date: Tue, 25 May 2021 10:29:36 +0100 Subject: [PATCH 1/6] Add basic terraform to manage existing resources --- .gitignore | 1 + terraform/locals.tf | 12 ++++ terraform/main.tf | 114 ++++++++++++++++++++++++++++++++++++++ terraform/outputs.tf | 8 +++ terraform/plan | Bin 0 -> 11887 bytes terraform/variables.tf | 15 +++++ terraform/vars/dev.tfvars | 3 + 7 files changed, 153 insertions(+) create mode 100644 terraform/locals.tf create mode 100644 terraform/main.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/plan create mode 100644 terraform/variables.tf create mode 100644 terraform/vars/dev.tfvars diff --git a/.gitignore b/.gitignore index c9ae635..0b8d65d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ local.settings.json node_modules/ +terraform/.terraform* diff --git a/terraform/locals.tf b/terraform/locals.tf new file mode 100644 index 0000000..2d85b72 --- /dev/null +++ b/terraform/locals.tf @@ -0,0 +1,12 @@ +locals { + region_short = substr(var.region, 0, 3) +} + +locals { + common_tags = { + product = "User Feedback" + owner = "mike.monteith@nhs.net" + expires = "Never" + environment = var.env + } +} diff --git a/terraform/main.tf b/terraform/main.tf new file mode 100644 index 0000000..cc89ad6 --- /dev/null +++ b/terraform/main.tf @@ -0,0 +1,114 @@ +# Configure the Azure provider +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = ">= 2.26" + } + } + + backend "azurerm" { + # We need to configure a different backend for different environments, so we use the following environment + # variables when running terraform init instead of putting the resource group and storage account + # config here as you normally would. + # Use these variables: + # -backend-config="resource_group_name=nhsuk-user-feedback-rg-tfstate-${ENV}-${REGION_SHORT}" + # -backend-config="storage_account_name=nhsukfeedbacktstate${ENV}" + container_name = "tstate" + key = "terraform.tfstate" + } +} + +provider "azurerm" { + features {} +} + +data "azurerm_resource_group" "rg" { + name = "nhsuk-user-feedback-rg-${var.env}-${local.region_short}" +} + +resource "azurerm_storage_account" "storage_account" { + name = "nhsukfeedback${var.env}${local.region_short}" + resource_group_name = data.azurerm_resource_group.rg.name + location = data.azurerm_resource_group.rg.location + account_tier = "Standard" + account_replication_type = "LRS" + + tags = local.common_tags +} + +resource "azurerm_app_service_plan" "service_plan" { + name = "nhsuk-user-feedback-plan-${var.env}-${local.region_short}" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + kind = "functionapp" + reserved = true + tags = local.common_tags + + sku { + tier = "Dynamic" + size = "Y1" + } +} + +resource "azurerm_application_insights" "application_insights" { + name = "nhsuk-user-feedback-func-${var.env}-${local.region_short}" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + tags = local.common_tags + application_type = "web" +} + +resource "azurerm_cosmosdb_account" "db" { + name = "nhsuk-user-feedback-db-${var.env}" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + tags = local.common_tags + offer_type = "Standard" + kind = "MongoDB" + + capabilities { + name = "EnableMongo" + } + + consistency_policy { + consistency_level = "Session" + max_interval_in_seconds = 5 + max_staleness_prefix = 100 + } + + geo_location { + location = data.azurerm_resource_group.rg.location + failover_priority = 0 + } +} + +resource "azurerm_function_app" "function_app" { + name = "nhsuk-user-feedback-func-${var.env}-${local.region_short}" + location = data.azurerm_resource_group.rg.location + resource_group_name = data.azurerm_resource_group.rg.name + app_service_plan_id = azurerm_app_service_plan.service_plan.id + storage_account_name = azurerm_storage_account.storage_account.name + storage_account_access_key = azurerm_storage_account.storage_account.primary_access_key + tags = local.common_tags + + os_type = "linux" + version = "~3" + + site_config { + cors { + allowed_origins = [var.enable_cors ? "*" : "https://www.nhs.uk"] + } + } + + app_settings = { + FUNCTIONS_WORKER_RUNTIME = "node" + APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.application_insights.instrumentation_key + APPLICATIONINSIGHTS_CONNECTION_STRING = azurerm_application_insights.application_insights.connection_string + WEBSITE_NODE_DEFAULT_VERSION: "~12" + WEBSITE_CONTENTAZUREFILECONNECTIONSTRING: azurerm_storage_account.storage_account.primary_connection_string + WEBSITE_CONTENTSHARE: "nhsuk-user-feedback-func-${var.env}-${local.region_short}" + MONGO_CONNECTION_STRING: "${azurerm_cosmosdb_account.db.connection_strings.0}&retrywrites=false&maxIdleTimeMS=120000" + SCM_DO_BUILD_DURING_DEPLOYMENT: "true" + } +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..ffd3939 --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,8 @@ +output "resource_group_name" { + value = data.azurerm_resource_group.rg.name + description = "Resource Group name" +} + +output "function_app_name" { + value = azurerm_function_app.function_app.name +} diff --git a/terraform/plan b/terraform/plan new file mode 100644 index 0000000000000000000000000000000000000000..3123aa9868018492bda1e48b2c696cd0e8f09683 GIT binary patch literal 11887 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 literal 0 HcmV?d00001 diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..98c5570 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,15 @@ +variable "env" { + description = "Environment short name e.g dev, stag, prod" + type = string +} + +variable "region" { + description = "Azure region" + type = string +} + +variable "enable_cors" { + description = "Allow all domains for cross-origin requests" + type = bool + default = false +} diff --git a/terraform/vars/dev.tfvars b/terraform/vars/dev.tfvars new file mode 100644 index 0000000..4616efa --- /dev/null +++ b/terraform/vars/dev.tfvars @@ -0,0 +1,3 @@ +env = "dev" +region = "uksouth" +enable_cors = true From 819396258f77fa02d7fb10669f386a93c54e86bb Mon Sep 17 00:00:00 2001 From: Mike Monteith Date: Tue, 25 May 2021 10:44:32 +0100 Subject: [PATCH 2/6] Remove ARM template --- AzureResourceGroup/template.json | 187 ------------------------------- 1 file changed, 187 deletions(-) delete mode 100644 AzureResourceGroup/template.json diff --git a/AzureResourceGroup/template.json b/AzureResourceGroup/template.json deleted file mode 100644 index de829c2..0000000 --- a/AzureResourceGroup/template.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "location": { - "type": "String", - "allowedValues": [ - "uksouth", - "ukwest" - ] - }, - "environment": { - "type": "String" - }, - "functionAppName": { - "type": "String" - }, - "hostingPlanSkuTier": { - "type": "String" - }, - "hostingPlanSkuName": { - "type": "String" - }, - "enableCORS": { - "type": "Bool" - }, - "buildTag": { - "type": "String" - }, - "expiresTag": { - "type": "String", - "defaultValue": "Never" - }, - "ownerTag": { - "type": "String" - } - }, - "variables": { - "locationAbbreviation": "[substring(parameters('location'), 0, 3)]", - "storageAccountName": "[concat('nhsukfeedback', parameters('environment'), variables('locationAbbreviation'))]", - "hostingPlanName": "[concat('nhsuk-user-feedback-plan-', parameters('environment'), '-', variables('locationAbbreviation'))]", - "appInsightsName": "[parameters('functionAppName')]", - "databaseName": "[concat('nhsuk-user-feedback-db-', parameters('environment'))]", - "tags": { - "Owner": "[parameters('ownerTag')]", - "Product": "User Feedback", - "Build": "[parameters('buildTag')]", - "Expires": "[parameters('expiresTag')]", - "Environment": "[parameters('environment')]" - } - }, - "resources": [ - { - "type": "Microsoft.Web/sites", - "apiVersion": "2019-08-01", - "name": "[parameters('functionAppName')]", - "kind": "functionapp,linux", - "location": "[parameters('location')]", - "dependsOn": [ - "[concat('microsoft.insights/components/', variables('appInsightsName'))]", - "[concat('Microsoft.Web/serverfarms/', variables('hostingPlanName'))]", - "[concat('Microsoft.Storage/storageAccounts/', variables('storageAccountName'))]", - "[concat('Microsoft.DocumentDB/databaseAccounts/', variables('databaseName'))]" - ], - "tags": "[variables('tags')]", - "properties": { - "name": "[parameters('functionAppName')]", - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('hostingPlanName'))]", - "siteConfig": { - "appSettings": [ - { - "name": "FUNCTIONS_EXTENSION_VERSION", - "value": "~3" - }, - { - "name": "FUNCTIONS_WORKER_RUNTIME", - "value": "node" - }, - { - "name": "APPINSIGHTS_INSTRUMENTATIONKEY", - "value": "[reference(concat('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').InstrumentationKey]" - }, - { - "name": "APPLICATIONINSIGHTS_CONNECTION_STRING", - "value": "[reference(concat('microsoft.insights/components/', variables('appInsightsName')), '2015-05-01').ConnectionString]" - }, - { - "name": "WEBSITE_NODE_DEFAULT_VERSION", - "value": "~12" - }, - { - "name": "AzureWebJobsStorage", - "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value,';EndpointSuffix=','core.windows.net')]" - }, - { - "name": "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING", - "value": "[concat('DefaultEndpointsProtocol=https;AccountName=',variables('storageAccountName'),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName')), '2019-06-01').keys[0].value,';EndpointSuffix=','core.windows.net')]" - }, - { - "name": "WEBSITE_CONTENTSHARE", - "value": "[toLower(parameters('functionAppName'))]" - }, - { - "name": "MONGO_CONNECTION_STRING", - "value": "[concat(listConnectionStrings(resourceId('Microsoft.DocumentDB/databaseAccounts', variables('databaseName')), '2019-12-12').connectionStrings[0].connectionString, '&retrywrites=false&maxIdleTimeMS=120000')]" - } - ], - "cors": { - "allowedOrigins": [ - "[if(parameters('enableCORS'), '*', 'https://www.nhs.uk')]" - ] - } - }, - "reserved": true - } - }, - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2019-06-01", - "name": "[variables('storageAccountName')]", - "location": "[parameters('location')]", - "tags": "[variables('tags')]", - "sku": { - "name": "Standard_LRS" - }, - "properties": { - "supportsHttpsTrafficOnly": true - } - }, - { - "type": "Microsoft.Web/serverfarms", - "apiVersion": "2019-08-01", - "name": "[variables('hostingPlanName')]", - "location": "[parameters('location')]", - "tags": "[variables('tags')]", - "sku": { - "Tier": "[parameters('hostingPlanSkuTier')]", - "Name": "[parameters('hostingPlanSkuName')]" - }, - "kind": "linux", - "properties": { - "reserved": true - } - }, - { - "type": "microsoft.insights/components", - "apiVersion": "2015-05-01", - "name": "[variables('appInsightsName')]", - "location": "[parameters('location')]", - "tags": "[variables('tags')]", - "kind": "web", - "properties": { - "Request_Source": "rest", - "Application_Type": "web" - } - }, - { - "type": "Microsoft.DocumentDB/databaseAccounts", - "apiVersion": "2020-03-01", - "name": "[variables('databaseName')]", - "location": "[parameters('location')]", - "tags": "[variables('tags')]", - "kind": "MongoDB", - "properties": { - "locations": [{ - "locationName": "[parameters('location')]" - }], - "databaseAccountOfferType": "Standard", - "capabilities": [ - { - "name": "EnableMongo" - } - ] - } - } - ], - "outputs": { - "functionAppName": { - "type": "string", - "value": "[parameters('functionAppName')]" - }, - "storageAccountName": { - "type": "string", - "value": "[variables('storageAccountName')]" - } - } -} From f218057b02adce4902b75cb28e70a0c67281fb8c Mon Sep 17 00:00:00 2001 From: Mike Monteith Date: Tue, 25 May 2021 10:47:51 +0100 Subject: [PATCH 3/6] Add deployment bash scripts --- .gitignore | 1 + scripts/create_terraform_state.sh | 53 +++++++++++++++++++++++++++ scripts/deploy.sh | 60 +++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100755 scripts/create_terraform_state.sh create mode 100755 scripts/deploy.sh diff --git a/.gitignore b/.gitignore index 0b8d65d..5c99c46 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ local.settings.json node_modules/ terraform/.terraform* +dist/ diff --git a/scripts/create_terraform_state.sh b/scripts/create_terraform_state.sh new file mode 100755 index 0000000..f8cdc0e --- /dev/null +++ b/scripts/create_terraform_state.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +for i in "$@" +do +case $i in + --env=*) + ENV="${i#*=}" + shift # past argument=value + ;; + --region=*) + REGION="${i#*=}" + shift # past argument=value + ;; + *) + # unknown option + ;; +esac +done + +if [[ -z "$ENV" ]]; then + echo "--env option must be provided" + exit 1 +fi +if [[ -z "$REGION" ]]; then + echo "--region option must be provided" + exit 1 +fi + +echo "ENV = $ENV" +echo "REGION = $REGION" + +set -e + + +RESOURCE_GROUP_NAME="nhsuk-user-feedback-rg-tfstate-$ENV-$REGION" +STORAGE_ACCOUNT_NAME="nhsukfeedbacktstate$ENV" +CONTAINER_NAME="tstate" + +# This resource group should have been created for you by the infrastructure team +# az group create --name $RESOURCE_GROUP_NAME --location $REGION + +# Create storage account +az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob + +# Get storage account key +ACCOUNT_KEY=$(az storage account keys list --resource-group $RESOURCE_GROUP_NAME --account-name $STORAGE_ACCOUNT_NAME --query '[0].value' -o tsv) + +# Create blob container +az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME --account-key $ACCOUNT_KEY + +echo "storage_account_name: $STORAGE_ACCOUNT_NAME" +echo "container_name: $CONTAINER_NAME" +echo "access_key: $ACCOUNT_KEY" diff --git a/scripts/deploy.sh b/scripts/deploy.sh new file mode 100755 index 0000000..cd1f242 --- /dev/null +++ b/scripts/deploy.sh @@ -0,0 +1,60 @@ +#!/bin/bash + +for i in "$@" +do +case $i in + --env=*) + ENV="${i#*=}" + shift # past argument=value + ;; + --region=*) + REGION="${i#*=}" + shift # past argument=value + ;; + *) + # unknown option + ;; +esac +done + +if [[ -z "$ENV" ]]; then + echo "--env option must be provided" + exit 1 +fi +if [[ -z "$REGION" ]]; then + echo "--region option must be provided" + exit 1 +fi + +echo "ENV = $ENV" +echo "REGION = $REGION" + +set -e + +mkdir -p "dist" +ZIP_FILE_PATH="dist/build.zip" + +# Create zip file of entire project, excluding some directories. +zip -r "$ZIP_FILE_PATH" . -x ".git/*" -x "dist/*" -x "terraform/*" -x "node_modules/*" -x "tests/*" + + +# Initialize terraform with Azure backend configs +terraform -chdir=./terraform init \ + -backend-config="resource_group_name=nhsuk-user-feedback-rg-tfstate-$ENV-$REGION" \ + -backend-config="storage_account_name=nhsukfeedbacktstate$ENV" + +# Apply terraform changes +terraform -chdir=./terraform apply \ + -auto-approve \ + -var-file="vars/$ENV.tfvars" + +# Get terraform outputs required for the functionapp deployment +RESOURCE_GROUP=$(terraform -chdir=./terraform output -raw resource_group_name) +FUNCTION_APP_NAME=$(terraform -chdir=./terraform output -raw function_app_name) + +# Deploy to the functionapp +az functionapp deployment source config-zip \ + -g $RESOURCE_GROUP \ + -n $FUNCTION_APP_NAME \ + --src $ZIP_FILE_PATH \ + --build-remote true From c75d0b4df36131021c1b058d3a1a8e04c90ab603 Mon Sep 17 00:00:00 2001 From: Mike Monteith Date: Tue, 25 May 2021 11:14:12 +0100 Subject: [PATCH 4/6] Use new deployment mechanism in azure pipeline --- azure-pipeline-templates/deploy.yaml | 31 +++++++++++ azure-pipelines.yml | 81 ++++++++++++++++++---------- 2 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 azure-pipeline-templates/deploy.yaml diff --git a/azure-pipeline-templates/deploy.yaml b/azure-pipeline-templates/deploy.yaml new file mode 100644 index 0000000..caf4d7f --- /dev/null +++ b/azure-pipeline-templates/deploy.yaml @@ -0,0 +1,31 @@ +steps: + +# Since this is a deployment stage, we need to checkout the code. Non-deployment stages do this by default +- checkout: self + +- task: AzureCLI@2 + inputs: + azureSubscription: 'nhsuk-user-feedback-${{parameters.environment}}' + scriptType: 'bash' + scriptLocation: 'inlineScript' + addSpnToEnvironment: true # adds $servicePrincipalId, $servicePrincipalKey and $tenantId to the env vars + inlineScript: | + set -e + # Set variables that terraform uses for azure authentication. + # Make these variables available to future jobs. + echo "##vso[task.setvariable variable=ARM_SUBSCRIPTION_ID]$(az account show --query 'id' --output tsv)" + echo "##vso[task.setvariable variable=ARM_TENANT_ID]$tenantId" + echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$servicePrincipalId" + echo "##vso[task.setvariable variable=ARM_CLIENT_SECRET]$servicePrincipalKey" + displayName: 'Get Azure auth variables' + +- task: AzureCLI@2 + inputs: + azureSubscription: 'nhsuk-user-feedback-${{parameters.environment}}' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + ./scripts/deploy.sh \ + --env=${{parameters.environment}} \ + --region=${{parameters.region}} + displayName: 'Run deploy script' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a26d048..c4866e9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -3,16 +3,17 @@ trigger: branches: include: - - master - - refs/tags/* + - master + - refs/tags/* + pr: - master - pool: - vmImage: 'ubuntu-latest' + vmImage: ubuntu-latest stages: + - stage: Test jobs: - job: Test @@ -24,33 +25,57 @@ stages: displayName: 'Install Node.js' - script: | - npm install + npm ci npm test displayName: 'npm test' -- stage: Build - displayName: Build stage +- stage: DevDeployment + displayName: 'Dev Deployment' + condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) + dependsOn: + - Test jobs: - - job: Build + - deployment: Deployment + environment: 'dev' + strategy: + runOnce: + deploy: + steps: + - template: azure-pipeline-templates/deploy.yaml + parameters: + environment: 'dev' + region: 'uks' - steps: - - task: NodeTool@0 - inputs: - versionSpec: '10.x' - displayName: 'Install Node.js' - - - script: | - npm install --production - displayName: 'npm install' +- stage: StagDeployment + displayName: 'Stag Deployment' + condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/')) + dependsOn: + - Test + jobs: + - deployment: Deployment + environment: 'staging' + strategy: + runOnce: + deploy: + steps: + - template: azure-pipeline-templates/deploy.yaml + parameters: + environment: 'stag' + region: 'uks' - - task: ArchiveFiles@2 - displayName: 'Archive files' - inputs: - rootFolderOrFile: '$(System.DefaultWorkingDirectory)' - includeRootFolder: false - archiveType: zip - archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip - replaceExistingArchive: true - - - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip - artifact: drop +- stage: ProdDeployment + displayName: 'Prod Deployment' + dependsOn: + - StagDeployment + - Test + jobs: + - deployment: Deployment + environment: 'production' + strategy: + runOnce: + deploy: + steps: + - template: azure-pipeline-templates/deploy.yaml + parameters: + environment: 'prod' + region: 'uks' From a379fc3cbab5aa1e0c08423ea3904e71335787f4 Mon Sep 17 00:00:00 2001 From: Mike Monteith Date: Tue, 25 May 2021 16:19:24 +0100 Subject: [PATCH 5/6] Add devops pipeline for terraform state creation --- terraform-state-pipeline.yaml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 terraform-state-pipeline.yaml diff --git a/terraform-state-pipeline.yaml b/terraform-state-pipeline.yaml new file mode 100644 index 0000000..d0c18cb --- /dev/null +++ b/terraform-state-pipeline.yaml @@ -0,0 +1,27 @@ +# Only allow this pipeline to be run manually +trigger: none + +pool: + vmImage: ubuntu-latest + +parameters: +- name: env + displayName: Environment (dev, stag, prod, etc.) + type: string +- name: region + displayName: Region (uks, ukw, etc.) + type: string + +steps: + +- task: AzureCLI@2 + inputs: + azureSubscription: 'nhsuk-user-feedback-${{ parameters.env }}' + scriptType: 'bash' + scriptLocation: 'inlineScript' + inlineScript: | + ./scripts/create_terraform_state.sh \ + --env=${{ parameters.env }} \ + --region=${{ parameters.region }} + displayName: 'Create terraform state resources' + From 05f70b4b8b398fdcd6b60900807df402296dd423 Mon Sep 17 00:00:00 2001 From: Mike Monteith Date: Wed, 2 Jun 2021 10:14:43 +0100 Subject: [PATCH 6/6] Fix no-newline-at-end-of-file --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8682ef0..730779b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ local.settings.json node_modules/ globalConfig.json terraform/.terraform* -dist/ \ No newline at end of file +dist/