diff --git a/terraform/azure/examples/aks/main.tf b/terraform/azure/examples/aks/main.tf new file mode 100644 index 0000000..df7c396 --- /dev/null +++ b/terraform/azure/examples/aks/main.tf @@ -0,0 +1,124 @@ +resource "azurerm_resource_group" "vnet" { + name = var.vnet_resource_group_name + location = var.location +} + +resource "azurerm_resource_group" "kube" { + name = var.kube_resource_group_name + location = var.location +} + +module "hub_network" { + source = "./modules/Vnets" + resource_group_name = azurerm_resource_group.vnet.name + location = var.location + vnet_name = var.hub_vnet_name + address_space = ["10.0.0.0/22"] + subnets = [ + { + name : "AzureFirewallSubnet" + address_prefixes : ["10.0.0.0/24"] + }, + { + name : "jumpbox-subnet" + address_prefixes : ["10.0.1.0/24"] + } + ] +} + +module "kube_network" { + source = "./modules/Vnets" + resource_group_name = azurerm_resource_group.kube.name + location = var.location + vnet_name = var.kube_vnet_name + address_space = ["10.0.4.0/22"] + subnets = [ + { + name : "aks-subnet" + address_prefixes : ["10.0.5.0/24"] + } + ] +} + +module "vnet_peering" { + source = "./modules/vnet_peering" + vnet_1_name = var.hub_vnet_name + vnet_1_id = module.hub_network.vnet_id + vnet_1_rg = azurerm_resource_group.vnet.name + vnet_2_name = var.kube_vnet_name + vnet_2_id = module.kube_network.vnet_id + vnet_2_rg = azurerm_resource_group.kube.name + peering_name_1_to_2 = "HubToSpoke1" + peering_name_2_to_1 = "Spoke1ToHub" +} + +module "firewall" { + source = "./modules/firewall" + resource_group = azurerm_resource_group.vnet.name + location = var.location + pip_name = "azureFirewalls-ip" + fw_name = "kubenetfw" + subnet_id = module.hub_network.subnet_ids["AzureFirewallSubnet"] +} + +module "routetable" { + source = "./modules/route_table" + resource_group = azurerm_resource_group.vnet.name + location = var.location + rt_name = "kubenetfw_fw_rt" + r_name = "kubenetfw_fw_r" + firewal_private_ip = module.firewall.fw_private_ip + subnet_id = module.kube_network.subnet_ids["aks-subnet"] +} + +data "azurerm_kubernetes_service_versions" "current" { + location = var.location + version_prefix = var.kube_version_prefix +} + +resource "azurerm_kubernetes_cluster" "privateaks" { + name = "private-aks" + location = var.location + kubernetes_version = data.azurerm_kubernetes_service_versions.current.latest_version + resource_group_name = azurerm_resource_group.kube.name + dns_prefix = "private-aks" + private_cluster_enabled = true + + default_node_pool { + name = "default" + node_count = var.nodepool_nodes_count + vm_size = var.nodepool_vm_size + vnet_subnet_id = module.kube_network.subnet_ids["aks-subnet"] + } + + identity { + type = "SystemAssigned" + } + + network_profile { + docker_bridge_cidr = var.network_docker_bridge_cidr + dns_service_ip = var.network_dns_service_ip + network_plugin = "azure" + outbound_type = "userDefinedRouting" + service_cidr = var.network_service_cidr + } + + depends_on = [module.routetable, ] + +} + +resource "azurerm_role_assignment" "netcontributor" { + role_definition_name = "Network Contributor" + scope = module.kube_network.subnet_ids["aks-subnet"] + principal_id = azurerm_kubernetes_cluster.privateaks.identity[0].principal_id +} + +module "jumpbox" { + source = "./modules/jumpbox" + location = var.location + resource_group = azurerm_resource_group.vnet.name + vnet_id = module.hub_network.vnet_id + subnet_id = module.hub_network.subnet_ids["jumpbox-subnet"] + dns_zone_name = join(".", slice(split(".", azurerm_kubernetes_cluster.privateaks.private_fqdn), 1, length(split(".", azurerm_kubernetes_cluster.privateaks.private_fqdn)))) + dns_zone_resource_group = azurerm_kubernetes_cluster.privateaks.node_resource_group +} \ No newline at end of file diff --git a/terraform/azure/examples/aks/outputs.tf b/terraform/azure/examples/aks/outputs.tf new file mode 100644 index 0000000..cad5db7 --- /dev/null +++ b/terraform/azure/examples/aks/outputs.tf @@ -0,0 +1,9 @@ +output "ssh_command" { + value = "ssh ${module.jumpbox.jumpbox_username}@${module.jumpbox.jumpbox_ip}" +} + +output "jumpbox_password" { + description = "Jumpbox Admin Passowrd" + value = module.jumpbox.jumpbox_password + sensitive = true +} \ No newline at end of file diff --git a/terraform/azure/examples/aks/provider.tf b/terraform/azure/examples/aks/provider.tf new file mode 100644 index 0000000..8c72cef --- /dev/null +++ b/terraform/azure/examples/aks/provider.tf @@ -0,0 +1,19 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "=3.0.0" + } + } + required_version = ">=1.5.0" +} + +provider "azurerm" { + features {} + + client_id = "54ae5591-08bf-4ca7-b546-afbd5fc2981c" + client_secret = ".h28Q~ahW2DXKY2PlN7fjM.988UtwYYC9B8k6cLD" + tenant_id = "9ca00460-1bd3-4c37-bdbb-432341e03634" + subscription_id = "7b133aa5-f11a-44e8-bf3f-d5e227b3fb3b" + +} diff --git a/terraform/azure/examples/aks/variables.tf b/terraform/azure/examples/aks/variables.tf new file mode 100644 index 0000000..33945bc --- /dev/null +++ b/terraform/azure/examples/aks/variables.tf @@ -0,0 +1,54 @@ +variable "vnet_resource_group_name" { + default = "rg-vnet" + description = "Vnet RG" +} + +variable "kube_resource_group_name" { + default = "rg-kube" + description = "Kube RG" +} + +variable "location" { + default = "Central India" + description = "Location" +} + +variable "hub_vnet_name" { + description = "Hub VNET name" + default = "hub1-firewalvnet" +} + +variable "kube_vnet_name" { + description = "AKS VNET name" + default = "spoke1-kubevnet" +} + +variable "kube_version_prefix" { + description = "AKS Kubernetes version prefix. Formatted '[Major].[Minor]' like '1.18'. Patch version part (as in '[Major].[Minor].[Patch]') will be set to latest automatically." + default = "1.25" +} + +variable "nodepool_nodes_count" { + description = "Default nodepool nodes count" + default = 1 +} + +variable "nodepool_vm_size" { + description = "Default nodepool VM size" + default = "Standard_D2_v2" +} + +variable "network_docker_bridge_cidr" { + description = "CNI Docker bridge cidr" + default = "172.17.0.1/16" +} + +variable "network_dns_service_ip" { + description = "CNI DNS service IP" + default = "10.2.0.10" +} + +variable "network_service_cidr" { + description = "CNI service cidr" + default = "10.2.0.0/24" +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/Vnets/main.tf b/terraform/azure/modules/aks_cluster/Vnets/main.tf new file mode 100644 index 0000000..8dc2525 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/Vnets/main.tf @@ -0,0 +1,16 @@ + +resource "azurerm_virtual_network" "vnets" { + name = var.vnet_name + address_space = var.address_space + location = var.location + resource_group_name = var.resource_group_name +} + +resource "azurerm_subnet" "subnet" { + for_each = { for subnet in var.subnets : subnet.name => subnet.address_prefixes } + + name = each.key + resource_group_name = var.resource_group_name + virtual_network_name = azurerm_virtual_network.vnets.name + address_prefixes = each.value +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/Vnets/outputs.tf b/terraform/azure/modules/aks_cluster/Vnets/outputs.tf new file mode 100644 index 0000000..a01f1ba --- /dev/null +++ b/terraform/azure/modules/aks_cluster/Vnets/outputs.tf @@ -0,0 +1,9 @@ +output vnet_id { + description = "Generated VNET ID" + value = azurerm_virtual_network.vnets.id +} + +output subnet_ids { + description = "Generated subnet IDs map" + value = { for subnet in azurerm_subnet.subnet : subnet.name => subnet.id } +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/Vnets/variables.tf b/terraform/azure/modules/aks_cluster/Vnets/variables.tf new file mode 100644 index 0000000..63c91ce --- /dev/null +++ b/terraform/azure/modules/aks_cluster/Vnets/variables.tf @@ -0,0 +1,24 @@ +variable "subnets" { + description = "Subnets configuration" + type = list(object({ + name = string + address_prefixes = list(string) + })) +} + +variable "vnet_name" { + description = "Virtual network name" +} + +variable "address_space" { + description = "Vnet address space" + type = list(string) +} + +variable "resource_group_name" { + description = "RG" +} + +variable "location" { + description = "Location" +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/firewall/main.tf b/terraform/azure/modules/aks_cluster/firewall/main.tf new file mode 100644 index 0000000..4507d24 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/firewall/main.tf @@ -0,0 +1,203 @@ + + +resource "azurerm_public_ip" "pip" { + name = var.pip_name + resource_group_name = var.resource_group + location = var.location + allocation_method = "Static" + sku = "Standard" +} + +resource "azurerm_firewall" "fw" { + name = var.fw_name + location = var.location + resource_group_name = var.resource_group + sku_name = "AZFW_VNet" + sku_tier = "Standard" + + ip_configuration { + name = "fw_ip_config" + subnet_id = var.subnet_id + public_ip_address_id = azurerm_public_ip.pip.id + } +} + +resource "azurerm_firewall_network_rule_collection" "time" { + name = "time" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 101 + action = "Allow" + + rule { + description = "aks node time sync rule" + name = "allow network" + source_addresses = ["*"] + destination_ports = ["123"] + destination_addresses = ["*"] + protocols = ["UDP"] + } +} + +resource "azurerm_firewall_network_rule_collection" "dns" { + name = "dns" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 102 + action = "Allow" + + rule { + description = "aks node dns rule" + name = "allow network" + source_addresses = ["*"] + destination_ports = ["53"] + destination_addresses = ["*"] + protocols = ["UDP"] + } +} + +resource "azurerm_firewall_network_rule_collection" "servicetags" { + name = "servicetags" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 110 + action = "Allow" + + rule { + description = "allow service tags" + name = "allow service tags" + source_addresses = ["*"] + destination_ports = ["*"] + protocols = ["Any"] + + destination_addresses = [ + "AzureContainerRegistry", + "MicrosoftContainerRegistry", + "AzureActiveDirectory" + ] + } +} + +resource "azurerm_firewall_application_rule_collection" "aksbasics" { + name = "aksbasics" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 101 + action = "Allow" + + rule { + name = "allow network" + source_addresses = ["*"] + + target_fqdns = [ + "*.cdn.mscr.io", + "mcr.microsoft.com", + "*.data.mcr.microsoft.com", + "management.azure.com", + "login.microsoftonline.com", + "acs-mirror.azureedge.net", + "dc.services.visualstudio.com", + "*.opinsights.azure.com", + "*.oms.opinsights.azure.com", + "*.microsoftonline.com", + "*.monitoring.azure.com", + ] + + protocol { + port = "80" + type = "Http" + } + + protocol { + port = "443" + type = "Https" + } + } +} + +resource "azurerm_firewall_application_rule_collection" "osupdates" { + name = "osupdates" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 102 + action = "Allow" + + rule { + name = "allow network" + source_addresses = ["*"] + + target_fqdns = [ + "download.opensuse.org", + "security.ubuntu.com", + "ntp.ubuntu.com", + "packages.microsoft.com", + "snapcraft.io" + ] + + protocol { + port = "80" + type = "Http" + } + + protocol { + port = "443" + type = "Https" + } + } +} + +resource "azurerm_firewall_application_rule_collection" "publicimages" { + name = "publicimages" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 103 + action = "Allow" + + rule { + name = "allow network" + source_addresses = ["*"] + + target_fqdns = [ + "auth.docker.io", + "registry-1.docker.io", + "production.cloudflare.docker.com" + ] + + protocol { + port = "80" + type = "Http" + } + + protocol { + port = "443" + type = "Https" + } + } +} + +resource "azurerm_firewall_application_rule_collection" "test" { + name = "test" + azure_firewall_name = azurerm_firewall.fw.name + resource_group_name = var.resource_group + priority = 104 + action = "Allow" + + rule { + name = "allow network" + source_addresses = ["*"] + + target_fqdns = [ + "*.bing.com" + ] + + protocol { + port = "80" + type = "Http" + } + + protocol { + port = "443" + type = "Https" + } + } +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/firewall/outputs.tf b/terraform/azure/modules/aks_cluster/firewall/outputs.tf new file mode 100644 index 0000000..7c9fb06 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/firewall/outputs.tf @@ -0,0 +1,3 @@ +output fw_private_ip { + value = azurerm_firewall.fw.ip_configuration[0].private_ip_address +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/firewall/variables.tf b/terraform/azure/modules/aks_cluster/firewall/variables.tf new file mode 100644 index 0000000..53530aa --- /dev/null +++ b/terraform/azure/modules/aks_cluster/firewall/variables.tf @@ -0,0 +1,25 @@ +variable resource_group { + description = "Resource group name" + type = string +} + +variable location { + description = "Location where Firewall will be deployed" + type = string +} + +variable pip_name { + description = "Firewal public IP name" + type = string + default = "azure-fw-ip" +} + +variable fw_name { + description = "Firewal name" + type = string +} + +variable subnet_id { + description = "Subnet ID" + type = string +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/jumpbox/main.tf b/terraform/azure/modules/aks_cluster/jumpbox/main.tf new file mode 100644 index 0000000..0c05eff --- /dev/null +++ b/terraform/azure/modules/aks_cluster/jumpbox/main.tf @@ -0,0 +1,103 @@ +resource "azurerm_public_ip" "pip" { + name = "vm-pip" + location = var.location + resource_group_name = var.resource_group + allocation_method = "Dynamic" +} + +resource "azurerm_network_security_group" "vm_sg" { + name = "vm-sg" + location = var.location + resource_group_name = var.resource_group + + 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 = "*" + } +} + +resource "azurerm_network_interface" "vm_nic" { + name = "vm-nic" + location = var.location + resource_group_name = var.resource_group + + ip_configuration { + name = "vmNicConfiguration" + subnet_id = var.subnet_id + private_ip_address_allocation = "Dynamic" + public_ip_address_id = azurerm_public_ip.pip.id + } +} + +resource "azurerm_network_interface_security_group_association" "sg_association" { + network_interface_id = azurerm_network_interface.vm_nic.id + network_security_group_id = azurerm_network_security_group.vm_sg.id +} + +resource "random_password" "adminpassword" { + keepers = { + resource_group = var.resource_group + } + + length = 10 + min_lower = 1 + min_upper = 1 + min_numeric = 1 +} + +resource "azurerm_linux_virtual_machine" "jumpbox" { + name = "jumpboxvm" + location = var.location + resource_group_name = var.resource_group + network_interface_ids = [azurerm_network_interface.vm_nic.id] + size = "Standard_DS1_v2" + computer_name = "jumpboxvm" + admin_username = var.vm_user + admin_password = random_password.adminpassword.result + disable_password_authentication = false + + os_disk { + name = "jumpboxOsDisk" + caching = "ReadWrite" + storage_account_type = "Premium_LRS" + } + + source_image_reference { + publisher = "Canonical" + offer = "UbuntuServer" + sku = "16.04.0-LTS" + version = "latest" + } + + provisioner "remote-exec" { + connection { + host = self.public_ip_address + type = "ssh" + user = var.vm_user + password = random_password.adminpassword.result + } + + inline = [ + "sudo apt-get update && sudo apt-get install -y apt-transport-https gnupg2", + "curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -", + "echo 'deb https://apt.kubernetes.io/ kubernetes-xenial main' | sudo tee -a /etc/apt/sources.list.d/kubernetes.list", + "sudo apt-get update", + "sudo apt-get install -y kubectl", + "curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash" + ] + } +} + +resource "azurerm_private_dns_zone_virtual_network_link" "hublink" { + name = "hubnetdnsconfig" + resource_group_name = var.dns_zone_resource_group + private_dns_zone_name = var.dns_zone_name + virtual_network_id = var.vnet_id +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/jumpbox/outputs.tf b/terraform/azure/modules/aks_cluster/jumpbox/outputs.tf new file mode 100644 index 0000000..84bd629 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/jumpbox/outputs.tf @@ -0,0 +1,14 @@ +output "jumpbox_ip" { + description = "Jumpbox VM IP" + value = azurerm_linux_virtual_machine.jumpbox.public_ip_address +} + +output "jumpbox_username" { + description = "Jumpbox VM username" + value = var.vm_user +} + +output "jumpbox_password" { + description = "Jumpbox VM admin password" + value = random_password.adminpassword.result +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/jumpbox/variables.tf b/terraform/azure/modules/aks_cluster/jumpbox/variables.tf new file mode 100644 index 0000000..4365f32 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/jumpbox/variables.tf @@ -0,0 +1,34 @@ +variable resource_group { + type = string +} + +variable location { + type = string +} + +variable vnet_id { + description = "ID of the VNET where jumpbox VM will be installed" + type = string +} + +variable subnet_id { + description = "ID of subnet where jumpbox VM will be installed" + type = string +} + +variable dns_zone_name { + description = "Private DNS Zone name to link jumpbox's vnet to" + type = string +} + +variable dns_zone_resource_group { + description = "Private DNS Zone resource group" + type = string +} + + +variable vm_user { + description = "Jumpbox VM user name" + type = string + default = "azureuser" +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/route_table/main.tf b/terraform/azure/modules/aks_cluster/route_table/main.tf new file mode 100644 index 0000000..e0e2a50 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/route_table/main.tf @@ -0,0 +1,17 @@ +resource "azurerm_route_table" "rt" { + name = var.rt_name + location = var.location + resource_group_name = var.resource_group + + route { + name = "kubenetfw_fw_r" + address_prefix = "0.0.0.0/0" + next_hop_type = "VirtualAppliance" + next_hop_in_ip_address = var.firewal_private_ip + } +} + +resource "azurerm_subnet_route_table_association" "aks_subnet_association" { + subnet_id = var.subnet_id + route_table_id = azurerm_route_table.rt.id +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/route_table/variables.tf b/terraform/azure/modules/aks_cluster/route_table/variables.tf new file mode 100644 index 0000000..9cdbe89 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/route_table/variables.tf @@ -0,0 +1,29 @@ +variable resource_group { + description = "Resource group where RouteTable will be deployed" + type = string +} + +variable location { + description = "Location where RouteTable will be deployed" + type = string +} + +variable rt_name { + description = "RouteTable name" + type = string +} + +variable r_name { + description = "AKS route name" + type = string +} + +variable firewal_private_ip { + description = "Firewall private IP" + type = string +} + +variable subnet_id { + description = "AKS subnet ID" + type = string +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/vnet_peering/main.tf b/terraform/azure/modules/aks_cluster/vnet_peering/main.tf new file mode 100644 index 0000000..3f3f343 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/vnet_peering/main.tf @@ -0,0 +1,13 @@ +resource "azurerm_virtual_network_peering" "peering" { + name = var.peering_name_1_to_2 + resource_group_name = var.vnet_1_rg + virtual_network_name = var.vnet_1_name + remote_virtual_network_id = var.vnet_2_id +} + +resource "azurerm_virtual_network_peering" "peering-back" { + name = var.peering_name_2_to_1 + resource_group_name = var.vnet_2_rg + virtual_network_name = var.vnet_2_name + remote_virtual_network_id = var.vnet_1_id +} \ No newline at end of file diff --git a/terraform/azure/modules/aks_cluster/vnet_peering/variables.tf b/terraform/azure/modules/aks_cluster/vnet_peering/variables.tf new file mode 100644 index 0000000..4362cf0 --- /dev/null +++ b/terraform/azure/modules/aks_cluster/vnet_peering/variables.tf @@ -0,0 +1,41 @@ +variable vnet_1_name { + description = "VNET 1 name" + type = string +} + +variable vnet_1_id { + description = "VNET 1 ID" + type = string +} + +variable vnet_1_rg { + description = "VNET 1 resource group" + type = string +} + +variable vnet_2_name { + description = "VNET 2 name" + type = string +} + +variable vnet_2_id { + description = "VNET 2 ID" + type = string +} + +variable vnet_2_rg { + description = "VNET 2 resource group" + type = string +} + +variable peering_name_1_to_2 { + description = "(optional) Peering 1 to 2 name" + type = string + default = "peering1to2" +} + +variable peering_name_2_to_1 { + description = "(optional) Peering 2 to 1 name" + type = string + default = "peering2to1" +} \ No newline at end of file