Skip to content
Merged
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
3 changes: 2 additions & 1 deletion .github/workflows/ci‑cd.yml → .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ jobs:
run: bicep build main.bicep

- name: Bicep what-if (simulated)
run: echo "Skipped: would run az deployment group what-if here"
run: |
echo "Skipped: would run az deployment group what-if here"

#
# OPTIONAL: OIDC Login to Azure (commented)
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ This is a mock setup intended as a skills demonstration. Actual deployment steps
- Traffic Manager actions are simulated via CLI echo commands
- SLO and rollback are represented by a test script with a forced failure

## Terraform modules

- **network**: Basic network module for hub-spoke architecture [(details)](./network/README.md)
- **aks**: AKS cluster module [(details)](./aks/README.md)
- **appgw**: Application Gateway module [(details)](./appgw/README.md)
- **cosmosdb**: Cosmos DB module [(details)](./cosmosdb/README.md)


## How to Use

```bash
Expand Down
49 changes: 49 additions & 0 deletions terraform/modules/aks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Moduel to provision AKS cluster



# Example usage
```hcl
# First create the networking
module "network" {
source = "./modules/network"
resource_group_id = azurerm_resource_group.example.id
location = "canadacentral"
vnet_name = "vnet-aks-prod"

subnets = {
"aks-subnet" = {
address_prefixes = ["10.0.1.0/24"]
service_endpoints = ["Microsoft.ContainerRegistry"]
# Enable required features for AKS
private_endpoint_network_policies_enabled = true
private_link_service_network_policies_enabled = true
}
}

tags = {
Environment = "Production"
Managed_By = "Terraform"
}
}

# Then deploy AKS using the subnet from the network module
module "aks" {
source = "./modules/aks"
cluster_name = "aks-prod"
resource_group_name = azurerm_resource_group.example.name
location = "canadacentral"
subnet_id = module.network.subnet_ids["aks-subnet"]

node_count = 2
node_vm_size = "Standard_D2s_v3"

tags = {
Environment = "Production"
Managed_By = "Terraform"
}

depends_on = [module.network]
}
```

37 changes: 24 additions & 13 deletions terraform/modules/aks/main.tf
Original file line number Diff line number Diff line change
@@ -1,28 +1,39 @@
variable "resource_group_name" {}
variable "location" {}
variable "cluster_name" {}


resource "azurerm_kubernetes_cluster" "aks" {
name = var.cluster_name
location = var.location
resource_group_name = var.resource_group_name
dns_prefix = "aks"
dns_prefix = "${var.cluster_name}-dns"
kubernetes_version = "1.27.7"

default_node_pool {
name = "default"
node_count = 1
vm_size = "Standard_B2s"
name = "default"
node_count = var.node_count
vm_size = var.node_vm_size
vnet_subnet_id = var.subnet_id
min_count = 1
max_count = 3
os_disk_size_gb = 50

tags = var.tags
}

network_profile {
network_plugin = "azure"
network_policy = "calico"
load_balancer_sku = "standard"
outbound_type = "loadBalancer"
}

identity {
type = "SystemAssigned"
}

tags = {
env = "demo"
}
tags = merge(var.tags, {
Environment = "Production"
Managed_By = "Terraform"
})
}

output "aks_id" {
value = azurerm_kubernetes_cluster.aks.id
}

16 changes: 16 additions & 0 deletions terraform/modules/aks/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
output "aks_id" {
description = "The ID of the AKS cluster"
value = azurerm_kubernetes_cluster.aks.id
}

output "kube_config" {
description = "Kubeconfig for the AKS cluster"
value = azurerm_kubernetes_cluster.aks.kube_config_raw
sensitive = true
}

output "cluster_identity" {
description = "System assigned identity of the AKS cluster"
value = azurerm_kubernetes_cluster.aks.identity[0].principal_id
}

38 changes: 38 additions & 0 deletions terraform/modules/aks/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
variable "resource_group_name" {
description = "Name of the resource group"
type = string
}

variable "location" {
description = "Azure region"
type = string
}

variable "cluster_name" {
description = "Name of the AKS cluster"
type = string
}

variable "subnet_id" {
description = "ID of the subnet where AKS should be deployed"
type = string
}

variable "node_count" {
description = "Number of nodes in the default node pool"
type = number
default = 1
}

variable "node_vm_size" {
description = "Size of the node VMs"
type = string
default = "Standard_D2s_v3"
}

variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}

66 changes: 66 additions & 0 deletions terraform/modules/appgw/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Application Gateway Module

This module creates an Azure Application Gateway with a WAF policy and a backend pool.

## Example usage
```hcl

# Create a resource group
resource "azurerm_resource_group" "rg" {
name = "example-resources"
location = "canadacentral"
tags = {
Environment = "Production"
Managed_By = "Terraform"
}
}

# First create the networking
module "network" {
source = "./modules/network"
resource_group_id = azurerm_resource_group.rg.id
location = "canadacentral"
vnet_name = "vnet-agw-prod"

subnets = {
"subnet" = {
address_prefixes = ["10.0.1.0/24"]
service_endpoints = ["Microsoft.ContainerRegistry"]
# Enable required features for AKS
private_endpoint_network_policies_enabled = true
private_link_service_network_policies_enabled = true
}
}

tags = {
Environment = "Production"
Managed_By = "Terraform"
}
}

# Create the Application Gateway

module "appgw" {
source = "./modules/appgw"
resource_group_name = azurerm_resource_group.rg.name
location = azurerm_resource_group.rg.location
vnet_name = module.network.vnet_name
subnet_name = module.network.subnet_name
subnet_id = module.network.subnet_id

appgw_name = "appgw-prod"
sku = "Standard_v2"
capacity = 2
tier = "Standard_v2"
frontend_ip_config = "frontend-ip-config"
frontend_port = "80"
backend_pool = "backend-pool"
backend_http_settings= "backend-http-settings"

tags = {
Environment = "Production"
Managed_By = "Terraform"
}
}
```

58 changes: 28 additions & 30 deletions terraform/modules/appgw/main.tf
Original file line number Diff line number Diff line change
@@ -1,71 +1,69 @@
variable "resource_group_name" {}
variable "location" {}
variable "gateway_name" {}

resource "azurerm_public_ip" "pip" {
name = "${var.gateway_name}-pip"
name = "${var.appgw_name}-pip"
location = var.location
resource_group_name = var.resource_group_name
allocation_method = "Static"
sku = "Standard"
tags = var.tags
}

resource "azurerm_application_gateway" "agw" {
name = var.gateway_name
name = var.appgw_name
location = var.location
resource_group_name = var.resource_group_name
tags = var.tags

sku {
name = "Standard_v2"
tier = "Standard_v2"
capacity = 1
name = var.sku
tier = var.tier
capacity = var.capacity
}

gateway_ip_configuration {
name = "gateway-ip"
subnet_id = "fake-subnet-id" # Use a dummy string for now
name = "gateway-ip-configuration"
subnet_id = var.subnet_id
}

frontend_port {
name = "http"
port = 80
name = "frontend-port"
port = var.frontend_port
}

frontend_ip_configuration {
name = "frontend"
name = var.frontend_ip_config
public_ip_address_id = azurerm_public_ip.pip.id
}

backend_address_pool {
name = "mock-backend-pool"
ip_addresses = "10.0.1.5" # dummy IP

name = var.backend_pool
}

backend_http_settings {
name = "mock-http-settings"
name = var.backend_http_settings
cookie_based_affinity = "Disabled"
path = "/"
port = 80
protocol = "Http"
request_timeout = 20
protocol = "Http"
request_timeout = 60
}

http_listener {
name = "listener"
frontend_ip_configuration_name = "frontend"
frontend_port_name = "http"
name = "basic-http-listener"
frontend_ip_configuration_name = var.frontend_ip_config
frontend_port_name = "frontend-port"
protocol = "Http"
}

request_routing_rule {
name = "rule1"
name = "basic-routing-rule"
rule_type = "Basic"
http_listener_name = "listener"
backend_address_pool_name = "mock-backend-pool"
backend_http_settings_name = "mock-http-settings"
http_listener_name = "basic-http-listener"
backend_address_pool_name = var.backend_pool
backend_http_settings_name = var.backend_http_settings
priority = 100
}
}

output "gateway_ip" {
value = azurerm_public_ip.pip.ip_address
lifecycle {
create_before_destroy = true
}
}
20 changes: 20 additions & 0 deletions terraform/modules/appgw/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Outputs
output "application_gateway_id" {
description = "The ID of the Application Gateway"
value = azurerm_application_gateway.agw.id
}

output "application_gateway_name" {
description = "The name of the Application Gateway"
value = azurerm_application_gateway.agw.name
}

output "public_ip_address" {
description = "The public IP address of the Application Gateway"
value = azurerm_public_ip.pip.ip_address
}

output "backend_pool_id" {
description = "The ID of the backend address pool"
value = one(azurerm_application_gateway.agw.backend_address_pool).id
}
Loading
Loading