Skip to content

Commit

Permalink
ER-704: Azure App Gateway (#793)
Browse files Browse the repository at this point in the history
* ER: Azure - Key Vault with SSL certificate

* ER: App Service Custom Domain

* ER: App Service SSL Certificate

* ER: App Gateway

* ER-704: Unit tests

* ER-704: Key Vault exception

---------

Co-authored-by: Sunny Sidhu <[email protected]>
  • Loading branch information
sunny-sidhu-and and sunny-sidhu-and authored Aug 29, 2023
1 parent 0c0f467 commit f857115
Show file tree
Hide file tree
Showing 12 changed files with 663 additions and 7 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/tf-azure-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ env:
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
TF_VAR_environment: ${{ vars.ENVIRONMENT }}
TF_VAR_resource_name_prefix: ${{ vars.RESOURCE_PREFIX }}
TF_VAR_kv_certificate_authority_username: ${{ secrets.CERTIFICATE_AUTHORITY_USERNAME }}
TF_VAR_kv_certificate_authority_password: ${{ secrets.CERTIFICATE_AUTHORITY_PASSWORD }}
TF_VAR_kv_certificate_authority_admin_email: ${{ vars.CERTIFICATE_AUTHORITY_ADMIN_EMAIL }}
TF_VAR_kv_certificate_authority_admin_first_name: ${{ secrets.CERTIFICATE_AUTHORITY_ADMIN_FIRST_NAME }}
TF_VAR_kv_certificate_authority_admin_last_name: ${{ secrets.CERTIFICATE_AUTHORITY_ADMIN_LAST_NAME }}
TF_VAR_kv_certificate_authority_admin_phone_no: ${{ secrets.CERTIFICATE_AUTHORITY_ADMIN_PHONE_NO }}
TF_VAR_kv_certificate_label: ${{ vars.CERTIFICATE_LABEL }}
TF_VAR_kv_certificate_subject: ${{ vars.CERTIFICATE_SUBJECT }}
TF_VAR_psqlfs_sku: ${{ vars.PSQLFS_SKU }}
TF_VAR_psqlfs_storage: ${{ vars.PSQLFS_STORAGE }}
TF_VAR_psqlfs_username: ${{ secrets.PSQLFS_USERNAME }}
Expand All @@ -49,6 +57,7 @@ env:
TF_VAR_webapp_docker_registry_url: https://ghcr.io
TF_VAR_webapp_docker_image: dfe-digital/early-years-foundation-recovery
TF_VAR_webapp_docker_image_tag: latest
TF_VAR_custom_domain_name: ${{ vars.CUSTOM_DOMAIN }}
TF_VAR_webapp_config_bot_token: ${{ secrets.WEBAPP_CONFIG_BOT_TOKEN }}
TF_VAR_webapp_config_contentful_environment: ${{ vars.WEBAPP_CONFIG_CONTENTFUL_ENVIRONMENT }}
TF_VAR_webapp_config_contentful_preview: ${{ vars.WEBAPP_CONFIG_CONTENTFUL_PREVIEW }}
Expand Down Expand Up @@ -180,4 +189,4 @@ jobs:

# Terraform Apply
- name: Terraform Apply
run: terraform apply -auto-approve tfplan/tfplan
run: terraform apply -auto-approve tfplan/tfplan
33 changes: 29 additions & 4 deletions terraform-azure/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ provider "azurerm" {
resource_group {
prevent_deletion_if_contains_resources = false
}

key_vault {
purge_soft_delete_on_destroy = true
recover_soft_deleted_key_vaults = true
}
}
}

Expand All @@ -24,10 +29,21 @@ resource "azurerm_resource_group" "rg" {
module "network" {
source = "./terraform-azure-network"

environment = var.environment
location = var.azure_region
resource_group = azurerm_resource_group.rg.name
resource_name_prefix = var.resource_name_prefix
environment = var.environment
location = var.azure_region
resource_group = azurerm_resource_group.rg.name
resource_name_prefix = var.resource_name_prefix
domain_name_label = var.webapp_name
kv_certificate_authority_label = "GlobalSignCA"
kv_certificate_authority_name = "GlobalSign"
kv_certificate_authority_username = var.kv_certificate_authority_username
kv_certificate_authority_password = var.kv_certificate_authority_password
kv_certificate_authority_admin_email = var.kv_certificate_authority_admin_email
kv_certificate_authority_admin_first_name = var.kv_certificate_authority_admin_first_name
kv_certificate_authority_admin_last_name = var.kv_certificate_authority_admin_last_name
kv_certificate_authority_admin_phone_no = var.kv_certificate_authority_admin_phone_no
kv_certificate_label = var.kv_certificate_label
kv_certificate_subject = var.kv_certificate_subject
}

# Create Database resources
Expand All @@ -52,6 +68,7 @@ module "database" {
module "webapp" {
source = "./terraform-azure-web"

environment = var.environment
asp_sku = var.asp_sku
webapp_worker_count = var.webapp_worker_count
location = var.azure_region
Expand All @@ -63,8 +80,16 @@ module "webapp" {
webapp_docker_image = var.webapp_docker_image
webapp_docker_image_tag = var.webapp_docker_image_tag
webapp_docker_registry_url = var.webapp_docker_registry_url
webapp_session_cookie_name = "_early_years_foundation_recovery_session"
webapp_custom_domain_name = var.webapp_custom_domain_name
webapp_custom_domain_cert_secret_label = var.kv_certificate_label
webapp_health_check_path = "/health"
webapp_health_check_eviction_time_in_min = 10
agw_subnet_id = module.network.agw_subnet_id
agw_pip_id = module.network.agw_pip_id
kv_id = module.network.kv_id
kv_cert_secret_id = module.network.kv_cert_secret_id
kv_mi_id = module.network.kv_mi_id
depends_on = [module.network, module.database]
}

Expand Down
1 change: 1 addition & 0 deletions terraform-azure/terraform-azure-app/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ resource "azurerm_log_analytics_workspace" "app_worker_logs" {
resource_group_name = var.resource_group
sku = "PerGB2018"
retention_in_days = 30
daily_quota_gb = 1

lifecycle {
ignore_changes = [tags]
Expand Down
136 changes: 136 additions & 0 deletions terraform-azure/terraform-azure-network/cert.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# Create Key Vault
data "azurerm_client_config" "az_config" {}

resource "azurerm_key_vault" "kv" {
# Key Vault only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

name = "${var.resource_name_prefix}-kv"
resource_group_name = var.resource_group
location = var.location
tenant_id = data.azurerm_client_config.az_config.tenant_id
enabled_for_disk_encryption = true
soft_delete_retention_days = 7
purge_protection_enabled = true
sku_name = "standard"

lifecycle {
ignore_changes = [tags]
}

#checkov:skip=CKV_AZURE_109:Access Policies configured
#checkov:skip=CKV_AZURE_189:Access Policies configured
#checkov:skip=CKV2_AZURE_32:VNET configuration adequate
}

resource "azurerm_user_assigned_identity" "kv_mi" {
# Key Vault only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

name = "${var.resource_name_prefix}-agw-mi"
location = var.location
resource_group_name = var.resource_group
}

resource "azurerm_key_vault_access_policy" "kv_ap" {
# Key Vault only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

key_vault_id = azurerm_key_vault.kv[0].id
tenant_id = data.azurerm_client_config.az_config.tenant_id
object_id = data.azurerm_client_config.az_config.object_id

secret_permissions = [
"Get"
]

certificate_permissions = [
"Create",
"Get",
"GetIssuers",
"Import",
"List",
"ListIssuers",
"ManageContacts",
"ManageIssuers",
"SetIssuers",
"Update"
]
}

resource "azurerm_key_vault_access_policy" "kv_mi_ap" {
# Key Vault only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

key_vault_id = azurerm_key_vault.kv[0].id
tenant_id = data.azurerm_client_config.az_config.tenant_id
object_id = azurerm_user_assigned_identity.kv_mi[0].principal_id

secret_permissions = [
"Get"
]

certificate_permissions = [
"Get"
]
}

resource "azurerm_key_vault_certificate_issuer" "kv_ca" {
# Key Vault only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

name = var.kv_certificate_authority_label
key_vault_id = azurerm_key_vault.kv[0].id
provider_name = var.kv_certificate_authority_name
account_id = var.kv_certificate_authority_username
password = var.kv_certificate_authority_password

admin {
email_address = var.kv_certificate_authority_admin_email
first_name = var.kv_certificate_authority_admin_first_name
last_name = var.kv_certificate_authority_admin_last_name
phone = var.kv_certificate_authority_admin_phone_no
}
}

resource "azurerm_key_vault_certificate" "kv_cert" {
# Key Vault only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

name = var.kv_certificate_label
key_vault_id = azurerm_key_vault.kv[0].id

certificate_policy {
issuer_parameters {
name = var.kv_certificate_authority_label
}

key_properties {
exportable = true
key_size = 2048
key_type = "RSA"
reuse_key = true
}

lifetime_action {
action {
action_type = "AutoRenew"
}

trigger {
days_before_expiry = 30
}
}

secret_properties {
content_type = "application/x-pkcs12"
}

x509_certificate_properties {
extended_key_usage = ["1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2"]
key_usage = ["digitalSignature", "keyEncipherment"]
subject = var.kv_certificate_subject
validity_in_months = 12
}
}
}
25 changes: 25 additions & 0 deletions terraform-azure/terraform-azure-network/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,29 @@ output "webapp_subnet_id" {
output "app_worker_subnet_id" {
description = "ID of the delegated Subnet for the Background Worker"
value = azurerm_subnet.app_worker_snet.id
}

output "agw_subnet_id" {
description = "ID of the Subnet for the App Gateway"
value = var.environment != "development" ? azurerm_subnet.agw_snet[0].id : null
}

output "agw_pip_id" {
description = "ID of the Public IP address for the App Gateway"
value = var.environment != "development" ? azurerm_public_ip.agw_pip[0].id : null
}

output "kv_id" {
description = "ID of the Key Vault"
value = var.environment != "development" ? azurerm_key_vault.kv[0].id : null
}

output "kv_cert_secret_id" {
description = "SSL certificate Secret ID"
value = var.environment != "development" ? azurerm_key_vault_certificate.kv_cert[0].secret_id : null
}

output "kv_mi_id" {
description = "ID of the Managed Identity for the Key Vault"
value = var.environment != "development" ? azurerm_user_assigned_identity.kv_mi[0].id : null
}
19 changes: 19 additions & 0 deletions terraform-azure/terraform-azure-network/pip.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Create PIP for App Gateway
resource "azurerm_public_ip" "agw_pip" {
# Application Gateway is not deployed to the Development subscription
count = var.environment != "development" ? 1 : 0

name = "${var.resource_name_prefix}-agw-pip"
resource_group_name = var.resource_group
location = var.location
allocation_method = "Static"
ip_version = "IPv4"
sku = "Standard"
sku_tier = "Regional"
zones = []
idle_timeout_in_minutes = 4

lifecycle {
ignore_changes = [tags]
}
}
55 changes: 55 additions & 0 deletions terraform-azure/terraform-azure-network/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,59 @@ variable "resource_group" {
variable "resource_name_prefix" {
description = "Prefix for resource names"
type = string
}

variable "domain_name_label" {
description = "DNS name label for assignment to Application Gateway"
type = string
}

variable "kv_certificate_authority_label" {
description = "Label for the Certificate Authority"
type = string
}

variable "kv_certificate_authority_name" {
description = "Name of the Certificate Authority"
type = string
}

variable "kv_certificate_authority_username" {
description = "Username for the Certificate provider"
type = string
}

variable "kv_certificate_authority_password" {
description = "Password the Certificate provider"
type = string
}

variable "kv_certificate_authority_admin_email" {
description = "Email Address of the Certificate Authority Admin"
type = string
}

variable "kv_certificate_authority_admin_first_name" {
description = "First Name of the Certificate Authority Admin"
type = string
}

variable "kv_certificate_authority_admin_last_name" {
description = "Last Name of the Certificate Authority Admin"
type = string
}

variable "kv_certificate_authority_admin_phone_no" {
description = "Phone No. of the Certificate Authority Admin"
type = string
}

variable "kv_certificate_label" {
description = "Label for the Certificate"
type = string
}

variable "kv_certificate_subject" {
description = "Subject of the Certificate"
type = string
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,19 @@ resource "azurerm_subnet" "app_worker_snet" {
}
}

#checkov:skip=CKV2_AZURE_31:NSG not required
}

# Create Subnet for App Gateway
resource "azurerm_subnet" "agw_snet" {
# Subnet only deployed to the Test and Production subscription
count = var.environment != "development" ? 1 : 0

name = "${var.resource_name_prefix}-agw-snet"
virtual_network_name = azurerm_virtual_network.vnet.name
resource_group_name = var.resource_group
address_prefixes = ["172.1.3.0/24"]
service_endpoints = ["Microsoft.Storage", "Microsoft.Web"]

#checkov:skip=CKV2_AZURE_31:NSG not required
}
Loading

0 comments on commit f857115

Please sign in to comment.