feat: comprehensive project structure improvements and Cloud for Sovereignty landing zone
- Add Cloud for Sovereignty landing zone architecture and deployment - Implement complete legal document management system - Reorganize documentation with improved navigation - Add infrastructure improvements (Dockerfiles, K8s, monitoring) - Add operational improvements (graceful shutdown, rate limiting, caching) - Create comprehensive project structure documentation - Add Azure deployment automation scripts - Improve repository navigation and organization
This commit is contained in:
64
infra/terraform/modules/regional-landing-zone/README.md
Normal file
64
infra/terraform/modules/regional-landing-zone/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Regional Landing Zone Module
|
||||
|
||||
Reusable Terraform module for deploying a complete landing zone in a single Azure region, following Cloud for Sovereignty and Well-Architected Framework principles.
|
||||
|
||||
## Features
|
||||
|
||||
- **Hub-and-Spoke Network Architecture**
|
||||
- Hub VNet with gateway, firewall, and management subnets
|
||||
- Spoke VNet with application, database, and storage subnets
|
||||
- VNet peering between hub and spoke
|
||||
|
||||
- **Security**
|
||||
- Azure Firewall for centralized security
|
||||
- Private endpoints for Key Vault and Storage
|
||||
- Network security groups
|
||||
|
||||
- **Compliance**
|
||||
- Customer-managed encryption
|
||||
- Data residency tags
|
||||
- Private endpoints for data sovereignty
|
||||
|
||||
- **Monitoring**
|
||||
- Regional Log Analytics Workspace
|
||||
- Application Insights ready
|
||||
|
||||
## Usage
|
||||
|
||||
```hcl
|
||||
module "west_europe_landing_zone" {
|
||||
source = "../../modules/regional-landing-zone"
|
||||
|
||||
region = "westeurope"
|
||||
environment = "dev"
|
||||
management_group_id = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
hub_vnet_address_space = "10.0.0.0/16"
|
||||
spoke_vnet_address_space = "10.1.0.0/16"
|
||||
|
||||
tags = {
|
||||
Project = "the-order"
|
||||
CostCenter = "engineering"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
- `region` (required): Azure region (must be non-US commercial)
|
||||
- `environment` (required): dev, stage, or prod
|
||||
- `management_group_id` (required): Management group ID
|
||||
- `hub_vnet_address_space` (optional): Hub VNet CIDR (default: 10.0.0.0/16)
|
||||
- `spoke_vnet_address_space` (optional): Spoke VNet CIDR (default: 10.1.0.0/16)
|
||||
- `tags` (optional): Additional tags
|
||||
|
||||
## Outputs
|
||||
|
||||
- `resource_group_name`: Resource group name
|
||||
- `hub_vnet_id`: Hub VNet ID
|
||||
- `spoke_vnet_id`: Spoke VNet ID
|
||||
- `firewall_id`: Azure Firewall ID
|
||||
- `key_vault_id`: Key Vault ID
|
||||
- `log_analytics_workspace_id`: Log Analytics Workspace ID
|
||||
- `storage_account_name`: Storage account name
|
||||
- `subnet_ids`: Map of subnet names to IDs
|
||||
|
||||
342
infra/terraform/modules/regional-landing-zone/main.tf
Normal file
342
infra/terraform/modules/regional-landing-zone/main.tf
Normal file
@@ -0,0 +1,342 @@
|
||||
# Regional Landing Zone Module
|
||||
# Deploys a complete landing zone for a single Azure region
|
||||
# Follows Cloud for Sovereignty and Well-Architected Framework principles
|
||||
|
||||
variable "region" {
|
||||
description = "Azure region (e.g., westeurope, northeurope)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains([
|
||||
"westeurope",
|
||||
"northeurope",
|
||||
"uksouth",
|
||||
"switzerlandnorth",
|
||||
"norwayeast",
|
||||
"francecentral",
|
||||
"germanywestcentral"
|
||||
], var.region)
|
||||
error_message = "Region must be a non-US commercial Azure region."
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (dev, stage, prod)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains(["dev", "stage", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, stage, or prod."
|
||||
}
|
||||
}
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Management group ID for this landing zone"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "hub_vnet_address_space" {
|
||||
description = "Address space for hub VNet"
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "spoke_vnet_address_space" {
|
||||
description = "Address space for spoke VNet"
|
||||
type = string
|
||||
default = "10.1.0.0/16"
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
# Local values for naming
|
||||
locals {
|
||||
region_abbrev = {
|
||||
westeurope = "we"
|
||||
northeurope = "ne"
|
||||
uksouth = "uk"
|
||||
switzerlandnorth = "ch"
|
||||
norwayeast = "no"
|
||||
francecentral = "fr"
|
||||
germanywestcentral = "de"
|
||||
}
|
||||
|
||||
env_abbrev = {
|
||||
dev = "dev"
|
||||
stage = "stg"
|
||||
prod = "prd"
|
||||
}
|
||||
|
||||
region_short = lookup(local.region_abbrev, var.region, "we")
|
||||
env_short = lookup(local.env_abbrev, var.environment, "dev")
|
||||
name_prefix = "az-${local.region_short}"
|
||||
|
||||
common_tags = merge(var.tags, {
|
||||
Region = var.region
|
||||
Environment = var.environment
|
||||
DataResidency = var.region
|
||||
ManagedBy = "terraform"
|
||||
LandingZone = "regional"
|
||||
SovereigntyLevel = "high"
|
||||
})
|
||||
}
|
||||
|
||||
# Resource Group
|
||||
resource "azurerm_resource_group" "landing_zone" {
|
||||
name = "${local.name_prefix}-rg-${local.env_short}-lz"
|
||||
location = var.region
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Hub Virtual Network
|
||||
resource "azurerm_virtual_network" "hub" {
|
||||
name = "${local.name_prefix}-vnet-${local.env_short}-hub"
|
||||
address_space = [var.hub_vnet_address_space]
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "HubNetwork"
|
||||
})
|
||||
}
|
||||
|
||||
# Hub Subnets
|
||||
resource "azurerm_subnet" "hub_gateway" {
|
||||
name = "GatewaySubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
address_prefixes = [cidrsubnet(var.hub_vnet_address_space, 8, 0)]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "hub_firewall" {
|
||||
name = "AzureFirewallSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
address_prefixes = [cidrsubnet(var.hub_vnet_address_space, 8, 1)]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "hub_management" {
|
||||
name = "ManagementSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
address_prefixes = [cidrsubnet(var.hub_vnet_address_space, 8, 2)]
|
||||
}
|
||||
|
||||
# Azure Firewall
|
||||
resource "azurerm_public_ip" "firewall" {
|
||||
name = "${local.name_prefix}-pip-${local.env_short}-fw"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
allocation_method = "Static"
|
||||
sku = "Standard"
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
resource "azurerm_firewall" "hub" {
|
||||
name = "${local.name_prefix}-fw-${local.env_short}-hub"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
sku_name = "AZFW_VNet"
|
||||
sku_tier = "Standard"
|
||||
|
||||
ip_configuration {
|
||||
name = "configuration"
|
||||
subnet_id = azurerm_subnet.hub_firewall.id
|
||||
public_ip_address_id = azurerm_public_ip.firewall.id
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Spoke Virtual Network
|
||||
resource "azurerm_virtual_network" "spoke" {
|
||||
name = "${local.name_prefix}-vnet-${local.env_short}-spoke"
|
||||
address_space = [var.spoke_vnet_address_space]
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "SpokeNetwork"
|
||||
})
|
||||
}
|
||||
|
||||
# Spoke Subnets
|
||||
resource "azurerm_subnet" "spoke_app" {
|
||||
name = "ApplicationSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
address_prefixes = [cidrsubnet(var.spoke_vnet_address_space, 8, 0)]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "spoke_db" {
|
||||
name = "DatabaseSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
address_prefixes = [cidrsubnet(var.spoke_vnet_address_space, 8, 1)]
|
||||
|
||||
delegation {
|
||||
name = "postgresql-delegation"
|
||||
service_delegation {
|
||||
name = "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
actions = [
|
||||
"Microsoft.Network/virtualNetworks/subnets/join/action",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "spoke_storage" {
|
||||
name = "StorageSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
address_prefixes = [cidrsubnet(var.spoke_vnet_address_space, 8, 2)]
|
||||
}
|
||||
|
||||
# VNet Peering: Hub to Spoke
|
||||
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
|
||||
name = "hub-to-spoke"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
remote_virtual_network_id = azurerm_virtual_network.spoke.id
|
||||
allow_forwarded_traffic = true
|
||||
allow_gateway_transit = true
|
||||
}
|
||||
|
||||
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
|
||||
name = "spoke-to-hub"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
remote_virtual_network_id = azurerm_virtual_network.hub.id
|
||||
allow_forwarded_traffic = true
|
||||
use_remote_gateways = false
|
||||
}
|
||||
|
||||
# Data source for current client config
|
||||
data "azurerm_client_config" "current" {}
|
||||
|
||||
# Key Vault (Regional)
|
||||
resource "azurerm_key_vault" "regional" {
|
||||
name = "${local.name_prefix}-kv-${local.env_short}-${local.region_short}"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
tenant_id = data.azurerm_client_config.current.tenant_id
|
||||
sku_name = "premium"
|
||||
|
||||
# Network ACLs - Private endpoint only
|
||||
network_acls {
|
||||
default_action = "Deny"
|
||||
bypass = "AzureServices"
|
||||
}
|
||||
|
||||
# Enable soft delete and purge protection
|
||||
soft_delete_retention_days = 90
|
||||
purge_protection_enabled = var.environment == "prod"
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "RegionalSecrets"
|
||||
})
|
||||
}
|
||||
|
||||
# Private Endpoint for Key Vault
|
||||
resource "azurerm_private_endpoint" "key_vault" {
|
||||
name = "${local.name_prefix}-pe-${local.env_short}-kv"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
subnet_id = azurerm_subnet.hub_management.id
|
||||
|
||||
private_service_connection {
|
||||
name = "kv-connection"
|
||||
private_connection_resource_id = azurerm_key_vault.regional.id
|
||||
subresource_names = ["vault"]
|
||||
is_manual_connection = false
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Log Analytics Workspace (Regional)
|
||||
resource "azurerm_log_analytics_workspace" "regional" {
|
||||
name = "${local.name_prefix}-law-${local.env_short}-${local.region_short}"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
sku = "PerGB2018"
|
||||
retention_in_days = var.environment == "prod" ? 90 : 30
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "RegionalLogging"
|
||||
})
|
||||
}
|
||||
|
||||
# Storage Account (Regional)
|
||||
resource "azurerm_storage_account" "regional" {
|
||||
name = "${local.name_prefix}sa${local.env_short}${local.region_short}"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
location = var.region
|
||||
account_tier = "Standard"
|
||||
account_replication_type = var.environment == "prod" ? "GRS" : "LRS"
|
||||
min_tls_version = "TLS1_2"
|
||||
allow_blob_public_access = false
|
||||
|
||||
# Customer-managed encryption
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
|
||||
blob_properties {
|
||||
versioning_enabled = true
|
||||
delete_retention_policy {
|
||||
days = var.environment == "prod" ? 90 : 30
|
||||
}
|
||||
}
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "RegionalStorage"
|
||||
})
|
||||
}
|
||||
|
||||
# Private Endpoint for Storage Account
|
||||
resource "azurerm_private_endpoint" "storage" {
|
||||
name = "${local.name_prefix}-pe-${local.env_short}-st"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
subnet_id = azurerm_subnet.spoke_storage.id
|
||||
|
||||
private_service_connection {
|
||||
name = "storage-connection"
|
||||
private_connection_resource_id = azurerm_storage_account.regional.id
|
||||
subresource_names = ["blob"]
|
||||
is_manual_connection = false
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "resource_group_name" {
|
||||
value = azurerm_resource_group.landing_zone.name
|
||||
}
|
||||
|
||||
output "hub_vnet_id" {
|
||||
value = azurerm_virtual_network.hub.id
|
||||
}
|
||||
|
||||
output "spoke_vnet_id" {
|
||||
value = azurerm_virtual_network.spoke.id
|
||||
}
|
||||
|
||||
output "key_vault_id" {
|
||||
value = azurerm_key_vault.regional.id
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
value = azurerm_log_analytics_workspace.regional.workspace_id
|
||||
}
|
||||
|
||||
output "storage_account_name" {
|
||||
value = azurerm_storage_account.regional.name
|
||||
}
|
||||
|
||||
94
infra/terraform/modules/regional-landing-zone/outputs.tf
Normal file
94
infra/terraform/modules/regional-landing-zone/outputs.tf
Normal file
@@ -0,0 +1,94 @@
|
||||
# Outputs for Regional Landing Zone Module
|
||||
|
||||
output "resource_group_name" {
|
||||
description = "Name of the resource group"
|
||||
value = azurerm_resource_group.landing_zone.name
|
||||
}
|
||||
|
||||
output "resource_group_id" {
|
||||
description = "ID of the resource group"
|
||||
value = azurerm_resource_group.landing_zone.id
|
||||
}
|
||||
|
||||
output "hub_vnet_id" {
|
||||
description = "ID of the hub virtual network"
|
||||
value = azurerm_virtual_network.hub.id
|
||||
}
|
||||
|
||||
output "hub_vnet_name" {
|
||||
description = "Name of the hub virtual network"
|
||||
value = azurerm_virtual_network.hub.name
|
||||
}
|
||||
|
||||
output "spoke_vnet_id" {
|
||||
description = "ID of the spoke virtual network"
|
||||
value = azurerm_virtual_network.spoke.id
|
||||
}
|
||||
|
||||
output "spoke_vnet_name" {
|
||||
description = "Name of the spoke virtual network"
|
||||
value = azurerm_virtual_network.spoke.name
|
||||
}
|
||||
|
||||
output "firewall_id" {
|
||||
description = "ID of the Azure Firewall"
|
||||
value = azurerm_firewall.hub.id
|
||||
}
|
||||
|
||||
output "firewall_private_ip" {
|
||||
description = "Private IP address of the Azure Firewall"
|
||||
value = azurerm_firewall.hub.ip_configuration[0].private_ip_address
|
||||
}
|
||||
|
||||
output "key_vault_id" {
|
||||
description = "ID of the Key Vault"
|
||||
value = azurerm_key_vault.regional.id
|
||||
}
|
||||
|
||||
output "key_vault_name" {
|
||||
description = "Name of the Key Vault"
|
||||
value = azurerm_key_vault.regional.name
|
||||
}
|
||||
|
||||
output "key_vault_uri" {
|
||||
description = "URI of the Key Vault"
|
||||
value = azurerm_key_vault.regional.vault_uri
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
description = "ID of the Log Analytics Workspace"
|
||||
value = azurerm_log_analytics_workspace.regional.workspace_id
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_name" {
|
||||
description = "Name of the Log Analytics Workspace"
|
||||
value = azurerm_log_analytics_workspace.regional.name
|
||||
}
|
||||
|
||||
output "storage_account_name" {
|
||||
description = "Name of the storage account"
|
||||
value = azurerm_storage_account.regional.name
|
||||
}
|
||||
|
||||
output "storage_account_id" {
|
||||
description = "ID of the storage account"
|
||||
value = azurerm_storage_account.regional.id
|
||||
}
|
||||
|
||||
output "storage_account_primary_endpoint" {
|
||||
description = "Primary endpoint of the storage account"
|
||||
value = azurerm_storage_account.regional.primary_blob_endpoint
|
||||
}
|
||||
|
||||
output "subnet_ids" {
|
||||
description = "Map of subnet names to IDs"
|
||||
value = {
|
||||
hub_gateway = azurerm_subnet.hub_gateway.id
|
||||
hub_firewall = azurerm_subnet.hub_firewall.id
|
||||
hub_management = azurerm_subnet.hub_management.id
|
||||
spoke_app = azurerm_subnet.spoke_app.id
|
||||
spoke_db = azurerm_subnet.spoke_db.id
|
||||
spoke_storage = azurerm_subnet.spoke_storage.id
|
||||
}
|
||||
}
|
||||
|
||||
51
infra/terraform/modules/regional-landing-zone/variables.tf
Normal file
51
infra/terraform/modules/regional-landing-zone/variables.tf
Normal file
@@ -0,0 +1,51 @@
|
||||
# Variables for Regional Landing Zone Module
|
||||
|
||||
variable "region" {
|
||||
description = "Azure region (e.g., westeurope, northeurope)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains([
|
||||
"westeurope",
|
||||
"northeurope",
|
||||
"uksouth",
|
||||
"switzerlandnorth",
|
||||
"norwayeast",
|
||||
"francecentral",
|
||||
"germanywestcentral"
|
||||
], var.region)
|
||||
error_message = "Region must be a non-US commercial Azure region."
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (dev, stage, prod)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains(["dev", "stage", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, stage, or prod."
|
||||
}
|
||||
}
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Management group ID for this landing zone"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "hub_vnet_address_space" {
|
||||
description = "Address space for hub VNet"
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "spoke_vnet_address_space" {
|
||||
description = "Address space for spoke VNet"
|
||||
type = string
|
||||
default = "10.1.0.0/16"
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
16
infra/terraform/modules/regional-landing-zone/versions.tf
Normal file
16
infra/terraform/modules/regional-landing-zone/versions.tf
Normal file
@@ -0,0 +1,16 @@
|
||||
# Terraform and Provider Version Constraints for Regional Landing Zone Module
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Configure Azure Provider (inherited from parent)
|
||||
# Provider configuration should be set in the calling module
|
||||
|
||||
Reference in New Issue
Block a user