diff --git a/intermediate/README.md b/intermediate/README.md
index 920a181..74a5640 100644
--- a/intermediate/README.md
+++ b/intermediate/README.md
@@ -1,8 +1,10 @@
# Terraform Intermediate Track
-## Kubernetes
-
-- [Deploy Your EKS Cluster using Terraform](https://github.com/collabnix/terraform/blob/master/beginners/aws/README.md)
+#### Azure
+
+ - [Terraform Provisioners in Azure](https://github.com/collabnix/terraform/blob/master/intermediate/azure/Terraform-Provisioners/)
+ - [Hub and Spoke Architecture](https://github.com/collabnix/terraform/blob/master/intermediate/azure/Hub-and-Spoke/)
+ - [Control egress traffic using Azure Firewall in Azure Kubernetes Service](https://github.com/collabnix/terraform/blob/master/intermediate/azure/AZ_FW-AKS-Egress/)
## Generic Terraform Related
@@ -10,6 +12,6 @@
- [Terraform Functions](https://github.com/collabnix/terraform/blob/master/intermediate/Terraform-Functions)
- Terraform Conditionals
- Using Remote Backend
-- Terraform Provisioners
+- [Terraform Provisioners](https://github.com/collabnix/terraform/blob/master/intermediate/azure/Terraform-Provisioners/)
- Multiple Providers
diff --git a/intermediate/azure/AZ_FW-AKS-Egress/README.md b/intermediate/azure/AZ_FW-AKS-Egress/README.md
new file mode 100644
index 0000000..2994609
--- /dev/null
+++ b/intermediate/azure/AZ_FW-AKS-Egress/README.md
@@ -0,0 +1,21 @@
+# Control egress traffic using Azure Firewall in Azure Kubernetes Service (AKS)
+
+Architecture :
+
+
+
+link : https://learn.microsoft.com/en-us/azure/aks/limit-egress-traffic
+
+required commands after `terraform apply` :
+
+1. Update Api Server Authorized IP Ranges
+```
+az aks update --resource-group rg-egress-001 --name aks-egress-001 --api-server-authorized-ip-ranges FW_PIP/32,Your_PIP/32
+```
+
+2. Create DNAT rule in Azure Firewall Policy and add Source Address of K8s Service and Destination address of Firewall Public IP
+```
+az network firewall policy rule-collection-group collection add-nat-collection -n nat_collection --collection-priority 10003 --policy-name {policy} -g {rg} --rule-collection-group-name {collectiongroup} --action DNAT --rule-name network_rule --description "test" --destination-addresses "202.120.36.15" --source-addresses "202.120.36.13" "202.120.36.14" --translated-address 128.1.1.1 --translated-port 1234 --destination-ports 12000 12001 --ip-protocols TCP UDP
+```
+or
+`Create DNAT rule using Azure Portal`
\ No newline at end of file
diff --git a/intermediate/azure/AZ_FW-AKS-Egress/main.tf b/intermediate/azure/AZ_FW-AKS-Egress/main.tf
new file mode 100644
index 0000000..2176cb4
--- /dev/null
+++ b/intermediate/azure/AZ_FW-AKS-Egress/main.tf
@@ -0,0 +1,159 @@
+resource "azurerm_resource_group" "rg" {
+ name = "rg-${var.postfix}"
+ location = var.location
+}
+
+resource "azurerm_virtual_network" "vnet" {
+ name = "vnet-${var.postfix}"
+ location = azurerm_resource_group.rg.location
+ resource_group_name = azurerm_resource_group.rg.name
+ address_space = ["10.42.0.0/16"]
+}
+
+resource "azurerm_subnet" "aks_subnet" {
+ name = "subnet-${var.postfix}"
+ resource_group_name = azurerm_resource_group.rg.name
+ virtual_network_name = azurerm_virtual_network.vnet.name
+ address_prefixes = ["10.42.1.0/24"]
+}
+
+resource "azurerm_subnet" "fw_subnet" {
+ name = "AzureFirewallSubnet"
+ resource_group_name = azurerm_resource_group.rg.name
+ virtual_network_name = azurerm_virtual_network.vnet.name
+ address_prefixes = ["10.42.2.0/24"]
+}
+
+resource "azurerm_public_ip" "fw_public_ip" {
+ name = "fw-pip-${var.postfix}"
+ resource_group_name = azurerm_resource_group.rg.name
+ location = azurerm_resource_group.rg.location
+ allocation_method = "Static"
+ sku = "Standard"
+}
+
+resource "azurerm_firewall" "fw" {
+ name = "fw-${var.postfix}"
+ resource_group_name = azurerm_resource_group.rg.name
+ location = azurerm_resource_group.rg.location
+ sku_name = "AZFW_VNet"
+ sku_tier = "Standard"
+ firewall_policy_id = azurerm_firewall_policy.fw_policy.id
+ ip_configuration {
+ name = "fw-ip-config-${var.postfix}"
+ subnet_id = azurerm_subnet.fw_subnet.id
+ public_ip_address_id = azurerm_public_ip.fw_public_ip.id
+ }
+}
+
+resource "azurerm_route_table" "fw_route_table" {
+ name = "fw-route-table-${var.postfix}"
+ resource_group_name = azurerm_resource_group.rg.name
+ location = azurerm_resource_group.rg.location
+
+ route {
+ name = "fw-route-${var.postfix}"
+ address_prefix = "0.0.0.0/0"
+ next_hop_type = "VirtualAppliance"
+ next_hop_in_ip_address = azurerm_firewall.fw.ip_configuration[0].private_ip_address
+ }
+
+ route {
+ name = "fw-route-internet-${var.postfix}"
+ address_prefix = "${azurerm_public_ip.fw_public_ip.ip_address}/32"
+ next_hop_type = "Internet"
+ next_hop_in_ip_address = null
+ }
+}
+
+resource "azurerm_firewall_policy" "fw_policy" {
+ name = "fw-policy-${var.postfix}"
+ resource_group_name = azurerm_resource_group.rg.name
+ location = azurerm_resource_group.rg.location
+ dns {
+ proxy_enabled = true
+ }
+}
+
+resource "azurerm_firewall_policy_rule_collection_group" "fw_rule_collection_group" {
+ name = "fw-rule-collection-group-${var.postfix}"
+ firewall_policy_id = azurerm_firewall_policy.fw_policy.id
+ priority = 100
+
+ network_rule_collection {
+ name = "aksfwnr"
+ priority = 200
+ action = "Allow"
+ rule {
+ name = "apiudp"
+ protocols = ["UDP"]
+ source_addresses = ["*"]
+ destination_addresses = ["AzureCloud.${var.location}"]
+ destination_ports = ["1194"]
+ }
+ rule {
+ name = "apitcp"
+ protocols = ["TCP"]
+ source_addresses = ["*"]
+ destination_addresses = ["AzureCloud.${var.location}"]
+ destination_ports = ["9000"]
+ }
+ rule {
+ name = "time"
+ protocols = ["UDP"]
+ source_addresses = ["*"]
+ destination_fqdns = ["ntp.ubuntu.com"]
+ destination_ports = ["123"]
+ }
+ }
+
+ application_rule_collection {
+ name = "aksfwar"
+ priority = 300
+ action = "Allow"
+ rule {
+ name = "fqdn"
+ source_addresses = ["*"]
+ destination_fqdn_tags = ["AzureKubernetesService"]
+ protocols {
+ type = "Http"
+ port = 80
+ }
+ protocols {
+ type = "Https"
+ port = 443
+ }
+ }
+ }
+}
+
+resource "azurerm_subnet_route_table_association" "aks_subnet_route_table_association" {
+ subnet_id = azurerm_subnet.aks_subnet.id
+ route_table_id = azurerm_route_table.fw_route_table.id
+}
+
+resource "azurerm_kubernetes_cluster" "aks" {
+ name = "aks-${var.postfix}"
+ location = azurerm_resource_group.rg.location
+ resource_group_name = azurerm_resource_group.rg.name
+ dns_prefix = "aks-${var.postfix}"
+
+ default_node_pool {
+ name = "default"
+ node_count = 1
+ vm_size = "Standard_D2_v2"
+ vnet_subnet_id = azurerm_subnet.aks_subnet.id
+ }
+
+ network_profile {
+ network_plugin = "azure"
+ outbound_type = "userDefinedRouting"
+ load_balancer_sku = "standard"
+ }
+
+ identity {
+ type = "SystemAssigned"
+ }
+
+ api_server_authorized_ip_ranges = api_server_authorized_ip_ranges = ["${azurerm_public_ip.fw_public_ip.ip_address}/32"]
+}
\ No newline at end of file
diff --git a/intermediate/azure/AZ_FW-AKS-Egress/provider.tf b/intermediate/azure/AZ_FW-AKS-Egress/provider.tf
new file mode 100644
index 0000000..1f6c150
--- /dev/null
+++ b/intermediate/azure/AZ_FW-AKS-Egress/provider.tf
@@ -0,0 +1,17 @@
+terraform {
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "~> 2.65"
+ }
+ }
+}
+
+provider "azurerm" {
+ features {}
+
+ client_id = ""
+ client_secret = ""
+ tenant_id = ""
+ subscription_id = ""
+}
\ No newline at end of file
diff --git a/intermediate/azure/AZ_FW-AKS-Egress/variables.tf b/intermediate/azure/AZ_FW-AKS-Egress/variables.tf
new file mode 100644
index 0000000..71f8e56
--- /dev/null
+++ b/intermediate/azure/AZ_FW-AKS-Egress/variables.tf
@@ -0,0 +1,7 @@
+variable "postfix" {
+ default = "egress-001"
+}
+
+variable "location" {
+ default = "canadacentral"
+}
\ No newline at end of file
diff --git a/intermediate/azure/Hub-and-Spoke/README.md b/intermediate/azure/Hub-and-Spoke/README.md
new file mode 100644
index 0000000..5f0cdcf
--- /dev/null
+++ b/intermediate/azure/Hub-and-Spoke/README.md
@@ -0,0 +1,7 @@
+# Create a hub and spoke hybrid network topology in Azure using Terraform
+
+Architecture :
+
+
+
+link : https://learn.microsoft.com/en-us/azure/developer/terraform/hub-spoke-introduction
\ No newline at end of file
diff --git a/intermediate/azure/Hub-and-Spoke/hub.tf b/intermediate/azure/Hub-and-Spoke/hub.tf
new file mode 100644
index 0000000..400db32
--- /dev/null
+++ b/intermediate/azure/Hub-and-Spoke/hub.tf
@@ -0,0 +1,166 @@
+resource "azurerm_resource_group" "hub_rg" {
+ name = var.hub_resource_group_name
+ location = var.resource_group_location
+}
+
+resource "azurerm_virtual_network" "hub_vnet" {
+ name = var.hub_vnet_name
+ location = var.resource_group_location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ address_space = ["192.168.0.0/16"]
+}
+
+resource "azurerm_subnet" "hub_subnet" {
+ name = var.hub_subnet_name
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ virtual_network_name = azurerm_virtual_network.hub_vnet.name
+ address_prefixes = ["192.168.1.0/24"]
+}
+
+resource "azurerm_subnet" "hub_gateway_subnet" {
+ name = "GatewaySubnet"
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ virtual_network_name = azurerm_virtual_network.hub_vnet.name
+ address_prefixes = ["192.168.255.224/27"]
+}
+
+# Create public IPs
+resource "azurerm_public_ip" "hub_public_ip" {
+ name = "hub-public-ip"
+ location = azurerm_resource_group.hub_rg.location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ allocation_method = "Static"
+}
+
+# Create Network Security Group and rule
+resource "azurerm_network_security_group" "hub_nsg" {
+ name = var.hub_nsg_name
+ location = azurerm_resource_group.hub_rg.location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+
+ security_rule {
+ name = "SSH"
+ priority = 1001
+ direction = "Inbound"
+ access = "Allow"
+ protocol = "Tcp"
+ source_port_range = "*"
+ destination_port_range = "22"
+ source_address_prefix = "*"
+ destination_address_prefix = "*"
+ }
+}
+
+# Create network interface
+resource "azurerm_network_interface" "hub_nic" {
+ name = var.hub_nic_name
+ location = azurerm_resource_group.hub_rg.location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+
+ ip_configuration {
+ name = "hub_nic_configuration"
+ subnet_id = azurerm_subnet.hub_subnet.id
+ private_ip_address_allocation = "Dynamic"
+ public_ip_address_id = azurerm_public_ip.hub_public_ip.id
+ }
+}
+
+# Connect the security group to the network interface
+resource "azurerm_network_interface_security_group_association" "hub_nisga" {
+ network_interface_id = azurerm_network_interface.hub_nic.id
+ network_security_group_id = azurerm_network_security_group.hub_nsg.id
+}
+
+# Create storage account for boot diagnostics
+resource "azurerm_storage_account" "hub_storage_account" {
+ name = var.hub_storage_account_name
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ location = azurerm_resource_group.hub_rg.location
+ account_tier = "Standard"
+ account_replication_type = "LRS"
+}
+
+# Create (and display) an SSH key
+resource "tls_private_key" "hub_ssh_key" {
+ algorithm = "RSA"
+ rsa_bits = 4096
+}
+
+# Create virtual machine
+resource "azurerm_linux_virtual_machine" "hub_vm" {
+ name = var.hub_vm_name
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ location = azurerm_resource_group.hub_rg.location
+ size = "Standard_D2s_v3"
+ network_interface_ids = [azurerm_network_interface.hub_nic.id]
+
+ os_disk {
+ name = "hubOsDisk"
+ caching = "ReadWrite"
+ storage_account_type = "Premium_LRS"
+ }
+
+ source_image_reference {
+ publisher = "Canonical"
+ offer = "0001-com-ubuntu-server-jammy"
+ sku = "22_04-lts-gen2"
+ version = "latest"
+ }
+
+ computer_name = "hubvm"
+ admin_username = "azureuser"
+ disable_password_authentication = true
+
+ admin_ssh_key {
+ username = "azureuser"
+ public_key = tls_private_key.hub_ssh_key.public_key_openssh
+ }
+
+ boot_diagnostics {
+ storage_account_uri = azurerm_storage_account.hub_storage_account.primary_blob_endpoint
+ }
+}
+
+resource "azurerm_public_ip" "hub_vpn_gateway_public_ip" {
+ name = "hub-vpn-gateway-public-ip"
+ location = azurerm_resource_group.hub_rg.location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ allocation_method = "Dynamic"
+}
+
+resource "azurerm_virtual_network_gateway" "hub_vpn_gateway" {
+ name = "hub-vpn-gateway"
+ location = azurerm_resource_group.hub_rg.location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ type = "Vpn"
+ vpn_type = "RouteBased"
+ sku = "VpnGw1"
+ ip_configuration {
+ name = "hub-vpn-gateway-ip-configuration"
+ public_ip_address_id = azurerm_public_ip.hub_vpn_gateway_public_ip.id
+ private_ip_address_allocation = "Dynamic"
+ subnet_id = azurerm_subnet.hub_gateway_subnet.id
+ }
+}
+
+resource "azurerm_virtual_network_gateway_connection" "hub-onprem-conn" {
+ name = "hub-onprem-conn"
+ location = azurerm_resource_group.hub_rg.location
+ resource_group_name = azurerm_resource_group.hub_rg.name
+ virtual_network_gateway_id = azurerm_virtual_network_gateway.hub_vpn_gateway.id
+ peer_virtual_network_gateway_id = azurerm_virtual_network_gateway.onprem_vpn_gateway.id
+ type = "Vnet2Vnet"
+ shared_key = "Azure@123"
+ routing_weight = 1
+}
+
+resource "azurerm_virtual_network_gateway_connection" "onprem-hub-conn" {
+ name = "onprem-hub-conn"
+ location = azurerm_resource_group.onprem_rg.location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ virtual_network_gateway_id = azurerm_virtual_network_gateway.onprem_vpn_gateway.id
+ peer_virtual_network_gateway_id = azurerm_virtual_network_gateway.hub_vpn_gateway.id
+ type = "Vnet2Vnet"
+ shared_key = "Azure@123"
+ routing_weight = 1
+}
\ No newline at end of file
diff --git a/intermediate/azure/Hub-and-Spoke/onprem.tf b/intermediate/azure/Hub-and-Spoke/onprem.tf
new file mode 100644
index 0000000..7e8b64c
--- /dev/null
+++ b/intermediate/azure/Hub-and-Spoke/onprem.tf
@@ -0,0 +1,143 @@
+resource "azurerm_resource_group" "onprem_rg" {
+ name = var.onprem_resource_group_name
+ location = var.resource_group_location
+}
+
+resource "azurerm_virtual_network" "onprem_vnet" {
+ name = var.onprem_vnet_name
+ location = var.resource_group_location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ address_space = ["172.168.0.0/16"]
+}
+
+resource "azurerm_subnet" "onprem_subnet" {
+ name = var.onprem_subnet_name
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ virtual_network_name = azurerm_virtual_network.onprem_vnet.name
+ address_prefixes = ["172.168.1.0/24"]
+}
+
+resource "azurerm_subnet" "onprem_gateway_subnet" {
+ name = "GatewaySubnet"
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ virtual_network_name = azurerm_virtual_network.onprem_vnet.name
+ address_prefixes = ["172.168.255.224/27"]
+}
+
+# Create public IPs
+resource "azurerm_public_ip" "onprem_public_ip" {
+ name = "onprem-public-ip"
+ location = azurerm_resource_group.onprem_rg.location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ allocation_method = "Static"
+}
+
+# Create Network Security Group and rule
+resource "azurerm_network_security_group" "onprem_nsg" {
+ name = var.onprem_nsg_name
+ location = azurerm_resource_group.onprem_rg.location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+
+ security_rule {
+ name = "SSH"
+ priority = 1001
+ direction = "Inbound"
+ access = "Allow"
+ protocol = "Tcp"
+ source_port_range = "*"
+ destination_port_range = "22"
+ source_address_prefix = "*"
+ destination_address_prefix = "*"
+ }
+}
+
+# Create network interface
+resource "azurerm_network_interface" "onprem_nic" {
+ name = var.onprem_nic_name
+ location = azurerm_resource_group.onprem_rg.location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+
+ ip_configuration {
+ name = "onprem_nic_configuration"
+ subnet_id = azurerm_subnet.onprem_subnet.id
+ private_ip_address_allocation = "Dynamic"
+ public_ip_address_id = azurerm_public_ip.onprem_public_ip.id
+ }
+}
+
+# Connect the security group to the network interface
+resource "azurerm_network_interface_security_group_association" "onprem_nisga" {
+ network_interface_id = azurerm_network_interface.onprem_nic.id
+ network_security_group_id = azurerm_network_security_group.onprem_nsg.id
+}
+
+# Create storage account for boot diagnostics
+resource "azurerm_storage_account" "onprem_storage_account" {
+ name = var.onprem_storage_account_name
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ location = azurerm_resource_group.onprem_rg.location
+ account_tier = "Standard"
+ account_replication_type = "LRS"
+}
+
+# Create (and display) an SSH key
+resource "tls_private_key" "onprem_ssh_key" {
+ algorithm = "RSA"
+ rsa_bits = 4096
+}
+
+# Create virtual machine
+resource "azurerm_linux_virtual_machine" "onprem_vm" {
+ name = var.onprem_vm_name
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ location = azurerm_resource_group.onprem_rg.location
+ size = "Standard_D2s_v3"
+ network_interface_ids = [azurerm_network_interface.onprem_nic.id]
+
+ os_disk {
+ name = "onpremOsDisk"
+ caching = "ReadWrite"
+ storage_account_type = "Premium_LRS"
+ }
+
+ source_image_reference {
+ publisher = "Canonical"
+ offer = "0001-com-ubuntu-server-jammy"
+ sku = "22_04-lts-gen2"
+ version = "latest"
+ }
+
+ computer_name = "onpremvm"
+ admin_username = "azureuser"
+ disable_password_authentication = true
+
+ admin_ssh_key {
+ username = "azureuser"
+ public_key = tls_private_key.onprem_ssh_key.public_key_openssh
+ }
+
+ boot_diagnostics {
+ storage_account_uri = azurerm_storage_account.onprem_storage_account.primary_blob_endpoint
+ }
+}
+
+resource "azurerm_public_ip" "onprem_vpn_gateway_public_ip" {
+ name = "onprem-vpn-gateway-public-ip"
+ location = azurerm_resource_group.onprem_rg.location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ allocation_method = "Dynamic"
+}
+
+resource "azurerm_virtual_network_gateway" "onprem_vpn_gateway" {
+ name = "onprem-vpn-gateway"
+ location = azurerm_resource_group.onprem_rg.location
+ resource_group_name = azurerm_resource_group.onprem_rg.name
+ type = "Vpn"
+ vpn_type = "RouteBased"
+ sku = "VpnGw1"
+ ip_configuration {
+ name = "onprem-vpn-gateway-ip-configuration"
+ public_ip_address_id = azurerm_public_ip.onprem_vpn_gateway_public_ip.id
+ subnet_id = azurerm_subnet.onprem_gateway_subnet.id
+ }
+}
\ No newline at end of file
diff --git a/intermediate/azure/Hub-and-Spoke/output.tf b/intermediate/azure/Hub-and-Spoke/output.tf
new file mode 100644
index 0000000..e69de29
diff --git a/intermediate/azure/Hub-and-Spoke/providers.tf b/intermediate/azure/Hub-and-Spoke/providers.tf
new file mode 100644
index 0000000..2aa6ace
--- /dev/null
+++ b/intermediate/azure/Hub-and-Spoke/providers.tf
@@ -0,0 +1,23 @@
+terraform {
+ required_providers {
+ azurerm = {
+ source = "hashicorp/azurerm"
+ version = "~> 2.65"
+ }
+ tls = {
+ source = "hashicorp/tls"
+ version = "~>4.0"
+ }
+ }
+
+ backend "azurerm" {
+ resource_group_name = "rg-demo-cc-001"
+ storage_account_name = "stdemocc001"
+ container_name = "contdemocc001"
+ key = "contdemocc001.tfstate"
+ }
+}
+
+provider "azurerm" {
+ features {}
+}
\ No newline at end of file
diff --git a/intermediate/azure/Hub-and-Spoke/spokes.tf b/intermediate/azure/Hub-and-Spoke/spokes.tf
new file mode 100644
index 0000000..d7ffae3
--- /dev/null
+++ b/intermediate/azure/Hub-and-Spoke/spokes.tf
@@ -0,0 +1,123 @@
+resource "azurerm_resource_group" "spoke_rg" {
+ name = var.spoke_resource_group_name
+ location = var.resource_group_location
+}
+
+resource "azurerm_virtual_network" "spoke_vnet" {
+ count = var.spoke_count
+ name = "${var.spoke_vnet_name_prefix}${format("%03d", count.index + 1)}"
+ location = var.resource_group_location
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+ address_space = ["10.${count.index + 1}.0.0/16"]
+}
+
+resource "azurerm_subnet" "spoke_subnet" {
+ count = var.spoke_count
+ name = "${var.spoke_subnet_name_prefix}${format("%03d", count.index + 1)}"
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+ virtual_network_name = azurerm_virtual_network.spoke_vnet[count.index].name
+ address_prefixes = ["10.${count.index + 1}.0.0/24"]
+}
+
+# Create public IPs
+resource "azurerm_public_ip" "spoke_public_ip" {
+ count = var.spoke_count
+ name = "${var.spoke_public_ip_name_prefix}${format("%03d", count.index + 1)}"
+ location = azurerm_resource_group.spoke_rg.location
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+ allocation_method = "Static"
+}
+
+# Create Network Security Group and rule
+resource "azurerm_network_security_group" "spoke_nsg" {
+ count = var.spoke_count
+ name = "${var.spoke_nsg_name_prefix}${format("%03d", count.index + 1)}"
+ location = azurerm_resource_group.spoke_rg.location
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+
+ security_rule {
+ name = "SSH"
+ priority = 1001
+ direction = "Inbound"
+ access = "Allow"
+ protocol = "Tcp"
+ source_port_range = "*"
+ destination_port_range = "22"
+ source_address_prefix = "*"
+ destination_address_prefix = "*"
+ }
+}
+
+# Create network interface
+resource "azurerm_network_interface" "spoke_nic" {
+ count = var.spoke_count
+ name = "${var.spoke_nic_name_prefix}${format("%03d", count.index + 1)}"
+ location = azurerm_resource_group.spoke_rg.location
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+
+ ip_configuration {
+ name = "spoke_nic_configuration"
+ subnet_id = azurerm_subnet.spoke_subnet[count.index].id
+ private_ip_address_allocation = "Dynamic"
+ public_ip_address_id = azurerm_public_ip.spoke_public_ip[count.index].id
+ }
+}
+
+# Connect the security group to the network interface
+resource "azurerm_network_interface_security_group_association" "spoke_nisga" {
+ count = var.spoke_count
+ network_interface_id = azurerm_network_interface.spoke_nic[count.index].id
+ network_security_group_id = azurerm_network_security_group.spoke_nsg[count.index].id
+}
+
+# Create storage account for boot diagnostics
+resource "azurerm_storage_account" "spoke_storage_account" {
+ count = var.spoke_count
+ name = "${var.spoke_storage_account_name_prefix}${format("%03d", count.index + 1)}"
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+ location = azurerm_resource_group.spoke_rg.location
+ account_tier = "Standard"
+ account_replication_type = "LRS"
+}
+
+# Create (and display) an SSH key
+resource "tls_private_key" "spoke_ssh_key" {
+ algorithm = "RSA"
+ rsa_bits = 4096
+}
+
+# Create virtual machine
+resource "azurerm_linux_virtual_machine" "spoke_vm" {
+ count = var.spoke_count
+ name = "${var.spoke_vm_name_prefix}${format("%03d", count.index + 1)}"
+ resource_group_name = azurerm_resource_group.spoke_rg.name
+ location = azurerm_resource_group.spoke_rg.location
+ size = "Standard_D2s_v3"
+ network_interface_ids = [azurerm_network_interface.spoke_nic[count.index].id]
+
+ os_disk {
+ name = "${var.spoke_vm_name_prefix}${format("%03d", count.index + 1)}_osdisk"
+ caching = "ReadWrite"
+ storage_account_type = "Standard_LRS"
+ }
+
+ source_image_reference {
+ publisher = "Canonical"
+ offer = "0001-com-ubuntu-server-jammy"
+ sku = "22_04-lts-gen2"
+ version = "latest"
+ }
+
+ computer_name = "spokevm"
+ admin_username = "azureuser"
+ disable_password_authentication = true
+
+ admin_ssh_key {
+ username = "azureuser"
+ public_key = tls_private_key.spoke_ssh_key.public_key_openssh
+ }
+
+ boot_diagnostics {
+ storage_account_uri = azurerm_storage_account.spoke_storage_account[count.index].primary_blob_endpoint
+ }
+}
\ No newline at end of file
diff --git a/intermediate/azure/Hub-and-Spoke/variables.tf b/intermediate/azure/Hub-and-Spoke/variables.tf
new file mode 100644
index 0000000..3e3de30
--- /dev/null
+++ b/intermediate/azure/Hub-and-Spoke/variables.tf
@@ -0,0 +1,129 @@
+variable "resource_group_location" {
+ type = string
+ default = "canadacentral"
+}
+
+variable "hub_resource_group_name" {
+ type = string
+ default = "rg-hub-cc-001"
+}
+
+variable "spoke_resource_group_name" {
+ type = string
+ default = "rg-spoke-cc-001"
+}
+
+variable "onprem_resource_group_name" {
+ type = string
+ default = "rg-onprem-cc-001"
+}
+
+variable "hub_vnet_name" {
+ type = string
+ default = "vnet-hub-cc-001"
+}
+
+variable "spoke_vnet_name_prefix" {
+ type = string
+ default = "vnet-spoke-cc-"
+}
+
+variable "onprem_vnet_name" {
+ type = string
+ default = "vnet-onprem-cc-001"
+}
+
+variable "hub_subnet_name" {
+ type = string
+ default = "subnet-hub-cc-001"
+}
+
+variable "spoke_subnet_name_prefix" {
+ type = string
+ default = "subnet-spoke-cc-"
+}
+
+variable "onprem_subnet_name" {
+ type = string
+ default = "subnet-onprem-cc-001"
+}
+
+variable "hub_vm_name" {
+ type = string
+ default = "vm-hub-cc-001"
+}
+
+variable "spoke_vm_name_prefix" {
+ type = string
+ default = "vm-spoke-cc-"
+}
+
+variable "onprem_vm_name" {
+ type = string
+ default = "vm-onprem-cc-001"
+}
+
+variable "spoke_count" {
+ type = number
+ default = 2
+}
+
+variable "hub_nsg_name" {
+ type = string
+ default = "nsg-hub-cc-001"
+}
+
+variable "hub_public_ip_name" {
+ type = string
+ default = "pip-hub-cc-001"
+}
+
+variable "onprem_nsg_name" {
+ type = string
+ default = "nsg-onprem-cc-001"
+}
+
+variable "onprem_public_ip_name" {
+ type = string
+ default = "pip-onprem-cc-001"
+}
+
+variable "spoke_nsg_name_prefix" {
+ type = string
+ default = "nsg-spoke-cc-"
+}
+
+variable "spoke_public_ip_name_prefix" {
+ type = string
+ default = "pip-spoke-cc-"
+}
+
+variable "hub_nic_name" {
+ type = string
+ default = "nic-hub-cc-001"
+}
+
+variable "onprem_nic_name" {
+ type = string
+ default = "nic-onprem-cc-001"
+}
+
+variable "spoke_nic_name_prefix" {
+ type = string
+ default = "nic-spoke-cc-"
+}
+
+variable "hub_storage_account_name" {
+ type = string
+ default = "sthubcc001"
+}
+
+variable "onprem_storage_account_name" {
+ type = string
+ default = "stonpremcc001"
+}
+
+variable "spoke_storage_account_name_prefix" {
+ type = string
+ default = "stspokecc"
+}
\ No newline at end of file