Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
**/bin/*
**/obj/*


# Ignore .env, except in web-frontend, where an
# empty copy is needed for vite-envs to work.
.env
Expand All @@ -24,3 +25,48 @@ e2e/.env

# Load test user pool (contains credentials)
load-tests/data/users.json

# Local .terraform directories
.terraform/

# .tfstate files
*.tfstate
*.tfstate.*

# Crash log files
crash.log
crash.*.log

# Exclude all .tfvars files, which are likely to contain sensitive data, such as
# password, private keys, and other secrets. These should not be part of version
# control as they are data points which are potentially sensitive and subject
# to change depending on the environment.
*.tfvars
*.tfvars.json

# Ignore override files as they are usually used to override resources locally and so
# are not checked in
override.tf
override.tf.json
*_override.tf
*_override.tf.json

# Ignore transient lock info files created by terraform apply
.terraform.tfstate.lock.info

# Include override files you do wish to add to version control using negated pattern
# !example_override.tf

# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
# example: *tfplan*

# Ignore CLI configuration files
.terraformrc
terraform.rc

# Optional: ignore graph output files generated by `terraform graph`
# *.dot

# Optional: ignore plan files saved before destroying Terraform configuration
# Uncomment the line below if you want to ignore planout files.
# planout
42 changes: 42 additions & 0 deletions shared/infra/azure/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions shared/infra/azure/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Shared Infrastructure Stack
# Deploys shared resources for integrated environments (dev, prod)

module "shared" {
source = "./modules/shared-resources"

env = var.env
location = var.location
dd_api_key = var.dd_api_key
dd_site = var.dd_site
vnet_address_space = var.vnet_address_space
container_apps_subnet_prefix = var.container_apps_subnet_prefix
private_endpoints_subnet_prefix = var.private_endpoints_subnet_prefix
postgresql_subnet_prefix = var.postgresql_subnet_prefix
postgresql_admin_username = var.postgresql_admin_username
}
12 changes: 12 additions & 0 deletions shared/infra/azure/modules/shared-resources/container-apps.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Shared Container App Environment
# All services in this environment can communicate internally via:
# http://<app-name>.internal.<environment-unique-id>.<region>.azurecontainerapps.io
resource "azurerm_container_app_environment" "shared" {
name = "cae-stickerlandia-${var.env}"
location = azurerm_resource_group.shared.location
resource_group_name = azurerm_resource_group.shared.name
log_analytics_workspace_id = azurerm_log_analytics_workspace.main.id
infrastructure_subnet_id = azurerm_subnet.container_apps.id
internal_load_balancer_enabled = false
tags = local.tags
}
21 changes: 21 additions & 0 deletions shared/infra/azure/modules/shared-resources/database.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# PostgreSQL Flexible Server (shared cluster - services create their own databases)
resource "azurerm_postgresql_flexible_server" "shared" {
name = "psql-stickerlandia-${var.env}"
location = azurerm_resource_group.shared.location
resource_group_name = azurerm_resource_group.shared.name
version = "16"
delegated_subnet_id = azurerm_subnet.postgresql.id
private_dns_zone_id = azurerm_private_dns_zone.postgresql.id
public_network_access_enabled = false
administrator_login = var.postgresql_admin_username
administrator_password = random_password.postgresql_admin_password.result
zone = "1"
storage_mb = 32768
storage_tier = "P4"
sku_name = var.postgresql_sku_name
tags = local.tags

depends_on = [
azurerm_private_dns_zone_virtual_network_link.postgresql
]
}
14 changes: 14 additions & 0 deletions shared/infra/azure/modules/shared-resources/frontdoor.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Azure Front Door Standard (shared entry point for all services)
resource "azurerm_cdn_frontdoor_profile" "shared" {
name = "afd-stickerlandia-${var.env}"
resource_group_name = azurerm_resource_group.shared.name
sku_name = "Standard_AzureFrontDoor"
tags = local.tags
}

# Shared Front Door Endpoint
resource "azurerm_cdn_frontdoor_endpoint" "shared" {
name = "stickerlandia-api-${var.env}"
cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.shared.id
tags = local.tags
}
85 changes: 85 additions & 0 deletions shared/infra/azure/modules/shared-resources/keyvault.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Random suffix for globally unique Key Vault name
resource "random_string" "kv_suffix" {
length = 6
special = false
upper = false
}

# Shared Key Vault for secrets management
resource "azurerm_key_vault" "shared" {
name = "kv-sticker-${var.env}-${random_string.kv_suffix.result}"
location = azurerm_resource_group.shared.location
resource_group_name = azurerm_resource_group.shared.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "standard"
soft_delete_retention_days = 7
purge_protection_enabled = false
public_network_access_enabled = true
rbac_authorization_enabled = true
tags = local.tags

network_acls {
bypass = "AzureServices"
default_action = "Allow"
}
}

# Private Endpoint for Key Vault
resource "azurerm_private_endpoint" "keyvault" {
name = "pe-keyvault-${var.env}"
location = azurerm_resource_group.shared.location
resource_group_name = azurerm_resource_group.shared.name
subnet_id = azurerm_subnet.private_endpoints.id
tags = local.tags

private_service_connection {
name = "keyvault-connection"
private_connection_resource_id = azurerm_key_vault.shared.id
is_manual_connection = false
subresource_names = ["vault"]
}

private_dns_zone_group {
name = "keyvault-dns-group"
private_dns_zone_ids = [azurerm_private_dns_zone.keyvault.id]
}
}

# Role assignment for current user to manage secrets during deployment
resource "azurerm_role_assignment" "keyvault_admin" {
scope = azurerm_key_vault.shared.id
role_definition_name = "Key Vault Secrets Officer"
principal_id = data.azurerm_client_config.current.object_id
}

# Store Datadog API Key in Key Vault (shared across all services)
resource "azurerm_key_vault_secret" "dd_api_key" {
name = "dd-api-key"
value = var.dd_api_key
key_vault_id = azurerm_key_vault.shared.id
tags = local.tags

depends_on = [
azurerm_role_assignment.keyvault_admin,
azurerm_private_endpoint.keyvault
]
}

# Store PostgreSQL admin password in Key Vault
resource "random_password" "postgresql_admin_password" {
length = 32
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}

resource "azurerm_key_vault_secret" "postgresql_admin_password" {
name = "postgresql-admin-password"
value = random_password.postgresql_admin_password.result
key_vault_id = azurerm_key_vault.shared.id
tags = local.tags

depends_on = [
azurerm_role_assignment.keyvault_admin,
azurerm_private_endpoint.keyvault
]
}
43 changes: 43 additions & 0 deletions shared/infra/azure/modules/shared-resources/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Shared Resources Module
# This module can be called from:
# 1. The shared infrastructure stack (for integrated environments like dev/prod)
# 2. Service stacks directly (for ephemeral/test environments)

terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.27"
}
random = {
source = "hashicorp/random"
version = "~> 3.8"
}
}
}

# Local values
locals {
resource_group_name = coalesce(var.resource_group_name, "rg-stickerlandia-shared-${var.env}")

default_tags = {
env = var.env
project = "stickerlandia"
scope = "shared"
source = "terraform"
}

tags = merge(local.default_tags, var.tags)
}

# Data sources
data "azurerm_subscription" "current" {}

data "azurerm_client_config" "current" {}

# Resource Group
resource "azurerm_resource_group" "shared" {
name = local.resource_group_name
location = var.location
tags = local.tags
}
8 changes: 8 additions & 0 deletions shared/infra/azure/modules/shared-resources/messaging.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Service Bus Namespace (shared - services create their own queues/topics)
resource "azurerm_servicebus_namespace" "shared" {
name = "sb-stickerlandia-${var.env}"
location = azurerm_resource_group.shared.location
resource_group_name = azurerm_resource_group.shared.name
sku = "Standard"
tags = local.tags
}
9 changes: 9 additions & 0 deletions shared/infra/azure/modules/shared-resources/monitoring.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Log Analytics Workspace (required by Container Apps)
resource "azurerm_log_analytics_workspace" "main" {
name = "log-stickerlandia-${var.env}"
location = azurerm_resource_group.shared.location
resource_group_name = azurerm_resource_group.shared.name
sku = "PerGB2018"
retention_in_days = 30
tags = local.tags
}
Loading
Loading