Skip to content

Commit

Permalink
ER-691: Host Web App in Azure (#696)
Browse files Browse the repository at this point in the history
* ER-695: Web App Terraform Script

* ER-695: Map of App Settings

* HTTP2 enabled

* ER-695: App Service logs

* ER-695: Configuration variables

* ER-695: Health Check

* ER-695: Startup commands

* ER-695: Continuous Deployment

* ER-695: Configurable Web App name

* ER-695: Logging recommendations

* ER-695: Formatting

---------

Co-authored-by: Sunny Sidhu <[email protected]>
  • Loading branch information
sunny-sidhu-and and sunny-sidhu-and authored Jul 14, 2023
1 parent ce5439b commit dadb986
Show file tree
Hide file tree
Showing 12 changed files with 513 additions and 3 deletions.
86 changes: 86 additions & 0 deletions .github/workflows/azure-deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: 'App Deploy [Azure - DEV]'

on:
workflow_dispatch:
push:
branches:
- main
paths-ignore:
- '**/*.md'
- .docker*
- .env.example
- .gitignore
- .pa11yci
- .tool-versions
- .yardopts
- bin/*
- docker-compose.*
- terraform/**
- terraform-azure/**
- uml/*

# Permissions for OIDC authentication
permissions:
id-token: write
contents: read

env:
ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
IMAGE_URL: ghcr.io/dfe-digital/early-years-foundation-recovery

jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: development

steps:
# Checkout the repository to the GitHub Actions runner
- name: Checkout Code
uses: actions/checkout@v3

# Login to Azure using OIDC
- name: Login to Azure CLI
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

# Login to the container registry
- name: Login to Github Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

# Create and boot Docker image builder
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
version: v0.9.1

# Build and push image
- name: Build and push Docker Image
uses: docker/build-push-action@v4
with:
target: app
context: .
push: true
build-args: |
BUILDKIT_INLINE_CACHE=1
SHA=${{ github.sha }}
cache-from: |
${{ env.IMAGE_URL }}:${{ github.sha }}
tags: |
${{ env.IMAGE_URL }}:${{ github.sha }}
${{ env.IMAGE_URL }}:latest
# Deploy container
- name: Deploy Container
uses: azure/webapps-deploy@v2
with:
app-name: ${{ vars.WEBAPP_NAME }}
images: '${{ env.IMAGE_URL }}:${{ github.sha }}'
26 changes: 25 additions & 1 deletion .github/workflows/tf-azure-deploy-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,31 @@ env:
TF_VAR_psqlfs_storage: ${{ vars.TF_VAR_PSQLFS_STORAGE }}
TF_VAR_psqlfs_username: ${{ secrets.TF_VAR_PSQLFS_USERNAME }}
TF_VAR_psqlfs_password: ${{ secrets.TF_VAR_PSQLFS_PASSWORD }}
TF_VAR_psqlfs_geo_redundant_backup: ${{ vars.TF_VAR_PSQLFS_GEO_REDUNDANT_BACKUP}}
TF_VAR_psqlfs_geo_redundant_backup: ${{ vars.TF_VAR_PSQLFS_GEO_REDUNDANT_BACKUP }}
TF_VAR_asp_sku: ${{ vars.TF_VAR_ASP_SKU }}
TF_VAR_webapp_name: ${{ vars.WEBAPP_NAME }}
TF_VAR_webapp_database_url: ${{ secrets.TF_VAR_WEBAPP_DATABASE_URL }}
TF_VAR_webapp_docker_registry_url: https://ghcr.io
TF_VAR_webapp_docker_registry_username: ${{ github.repository_owner }}
TF_VAR_webapp_docker_registry_password: ${{ secrets.GITHUB_TOKEN }}
TF_VAR_webapp_docker_image_url: ghcr.io/dfe-digital/early-years-foundation-recovery
TF_VAR_webapp_docker_image_tag: latest
TF_VAR_webapp_config_bot_token: ${{ secrets.TF_VAR_WEBAPP_CONFIG_BOT_TOKEN }}
TF_VAR_webapp_config_contentful_environment: ${{ vars.TF_VAR_WEBAPP_CONFIG_CONTENTFUL_ENVIRONMENT }}
TF_VAR_webapp_config_contentful_preview: ${{ vars.TF_VAR_WEBAPP_CONFIG_CONTENTFUL_PREVIEW }}
TF_VAR_webapp_config_domain: ${{ vars.TF_VAR_WEBAPP_CONFIG_DOMAIN }}
TF_VAR_webapp_config_editor: ${{ vars.TF_VAR_WEBAPP_CONFIG_EDITOR }}
TF_VAR_webapp_config_feedback_url: ${{ vars.TF_VAR_WEBAPP_CONFIG_FEEDBACK_URL }}
TF_VAR_webapp_config_grover_no_sandbox: ${{ vars.TF_VAR_WEBAPP_CONFIG_GROVER_NO_SANDBOX }}
TF_VAR_webapp_config_google_cloud_bucket: ${{ vars.TF_VAR_WEBAPP_CONFIG_GOOGLE_CLOUD_BUCKET }}
TF_VAR_webapp_config_node_env: ${{ vars.TF_VAR_WEBAPP_CONFIG_NODE_ENV }}
TF_VAR_webapp_config_rails_env: ${{ vars.TF_VAR_WEBAPP_CONFIG_RAILS_ENV }}
TF_VAR_webapp_config_rails_log_to_stdout: ${{ vars.TF_VAR_WEBAPP_CONFIG_RAILS_LOG_TO_STDOUT }}
TF_VAR_webapp_config_rails_master_key: ${{ secrets.TF_VAR_WEBAPP_CONFIG_RAILS_MASTER_KEY }}
TF_VAR_webapp_config_rails_max_threads: ${{ vars.TF_VAR_WEBAPP_CONFIG_RAILS_MAX_THREADS }}
TF_VAR_webapp_config_rails_serve_static_files: ${{ vars.TF_VAR_WEBAPP_CONFIG_RAILS_SERVE_STATIC_FILES }}
TF_VAR_webapp_config_training_modules: ${{ vars.TF_VAR_WEBAPP_CONFIG_TRAINING_MODULES }}
TF_VAR_webapp_config_web_concurrency: ${{ vars.TF_VAR_WEBAPP_CONFIG_WEB_CONCURRENCY }}

jobs:
terraform-plan:
Expand Down
2 changes: 2 additions & 0 deletions docker-entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ else
bundle exec rails db:create db:migrate
fi

bundle exec rails db:prepare assets:precompile

exec bundle exec "$@"
52 changes: 51 additions & 1 deletion terraform-azure/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
provider "azurerm" {
use_oidc = true
features {}

features {
resource_group {
prevent_deletion_if_contains_resources = false
}
}
}

locals {
Expand All @@ -23,6 +28,10 @@ resource "azurerm_resource_group" "rg" {

tags = merge(local.common_tags, {
})

lifecycle {
ignore_changes = [tags]
}
}

# Create Network resources
Expand All @@ -49,4 +58,45 @@ module "database" {
psqlfs_password = var.psqlfs_password
psqlfs_geo_redundant_backup = var.psqlfs_geo_redundant_backup
depends_on = [module.network]
}

# Create Web Application resources
module "webapp" {
source = "./terraform-azure-web"

asp_sku = var.asp_sku
location = var.azure_region
resource_group = azurerm_resource_group.rg.name
resource_name_prefix = var.resource_name_prefix
webapp_subnet_id = module.network.webapp_subnet_id
webapp_name = var.webapp_name
webapp_app_settings = {
"DATABASE_URL" = var.webapp_database_url
"DOCKER_REGISTRY_SERVER_URL" = var.webapp_docker_registry_url
"DOCKER_REGISTRY_SERVER_USERNAME" = var.webapp_docker_registry_username
"DOCKER_REGISTRY_SERVER_PASSWORD" = var.webapp_docker_registry_password
"WEBSITES_ENABLE_APP_SERVICE_STORAGE" = "false"
"GOVUK_APP_DOMAIN" = "london.cloudapps.digital" #TODO: Remove this dependency post-migration to Azure
"GOVUK_WEBSITE_ROOT" = "ey-recovery-dev" #TODO: Remove this dependency post-migration to Azure
"BOT_TOKEN" = var.webapp_config_bot_token
"CONTENTFUL_ENVIRONMENT" = var.webapp_config_contentful_environment
"CONTENTFUL_PREVIEW" = var.webapp_config_contentful_preview
"DOMAIN" = var.webapp_config_domain
"EDITOR" = var.webapp_config_editor
"FEEDBACK_URL" = var.webapp_config_feedback_url
"GROVER_NO_SANDBOX" = var.webapp_config_grover_no_sandbox
"GOOGLE_CLOUD_BUCKET" = var.webapp_config_google_cloud_bucket
"NODE_ENV" = var.webapp_config_node_env
"RAILS_ENV" = var.webapp_config_rails_env
"RAILS_LOG_TO_STDOUT" = var.webapp_config_rails_log_to_stdout
"RAILS_MASTER_KEY" = var.webapp_config_rails_master_key
"RAILS_MAX_THREADS" = var.webapp_config_rails_max_threads
"RAILS_SERVE_STATIC_FILES" = var.webapp_config_rails_serve_static_files
"TRAINING_MODULES" = var.webapp_config_training_modules
"WEB_CONCURRENCY" = var.webapp_config_web_concurrency
}
webapp_docker_image_url = var.webapp_docker_image_url
webapp_docker_image_tag = var.webapp_docker_image_tag
webapp_health_check_path = "/health"
depends_on = [module.network, module.database]
}
4 changes: 4 additions & 0 deletions terraform-azure/terraform-azure-database/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ resource "azurerm_postgresql_flexible_server" "psqlfs" {
backup_retention_days = 7
geo_redundant_backup_enabled = var.psqlfs_geo_redundant_backup

lifecycle {
ignore_changes = [tags]
}

#checkov:skip=CKV_AZURE_136:Geo-redundant backup is configurable depending on environment
}

Expand Down
32 changes: 32 additions & 0 deletions terraform-azure/terraform-azure-network/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ resource "azurerm_virtual_network" "vnet" {
location = var.location
resource_group_name = var.resource_group
address_space = ["172.1.0.0/16"]

lifecycle {
ignore_changes = [tags]
}
}

# Create Subnet for Database Server
Expand All @@ -30,6 +34,10 @@ resource "azurerm_subnet" "psqlfs_snet" {
resource "azurerm_private_dns_zone" "psqlfs_dnsz" {
name = "${var.resource_name_prefix}.postgres.database.azure.com"
resource_group_name = var.resource_group

lifecycle {
ignore_changes = [tags]
}
}

# Link the Private DNS Zone to the Virtual Network
Expand All @@ -38,4 +46,28 @@ resource "azurerm_private_dns_zone_virtual_network_link" "psqlfs_dnsz_vnetl" {
private_dns_zone_name = azurerm_private_dns_zone.psqlfs_dnsz.name
virtual_network_id = azurerm_virtual_network.vnet.id
resource_group_name = var.resource_group

lifecycle {
ignore_changes = [tags]
}
}

# Create Subnet for Web App
resource "azurerm_subnet" "webapp_snet" {
name = "${var.resource_name_prefix}-webapp-snet"
virtual_network_name = azurerm_virtual_network.vnet.name
resource_group_name = var.resource_group
address_prefixes = ["172.1.1.0/26"]
service_endpoints = ["Microsoft.Storage"]

delegation {
name = "${var.resource_name_prefix}-webapp-dn"

service_delegation {
name = "Microsoft.Web/serverFarms"
actions = ["Microsoft.Network/virtualNetworks/subnets/action"]
}
}

#checkov:skip=CKV2_AZURE_31:NSG not required
}
5 changes: 5 additions & 0 deletions terraform-azure/terraform-azure-network/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ output "psqlfs_subnet_id" {
output "psqlfs_dns_zone_id" {
description = "ID of the Private DNS Zone for the Database Server"
value = azurerm_private_dns_zone.psqlfs_dnsz.id
}

output "webapp_subnet_id" {
description = "ID of the delegated Subnet for the Web Application"
value = azurerm_subnet.webapp_snet.id
}
21 changes: 21 additions & 0 deletions terraform-azure/terraform-azure-web/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2022 DFE-Digital

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
3 changes: 3 additions & 0 deletions terraform-azure/terraform-azure-web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Azure Web Module

This module provisions a new Azure App Service to host a Docker container for a Web Application.
Loading

0 comments on commit dadb986

Please sign in to comment.