Introduction :-
We will see here how to build with Terragrunt an #Azure Application Gateway with:
A Monitoring Dashboard hosted on a Log Analytics Workspace.
How to access TLS/SSL certificates from Key Vault as a safeguard of our Web
An #Azure Application Gateway is a PaaS service that acts as a web traffic load balancer (layer 4 and layer 7), all its features are available here for information.
The following diagram illustrates a sample network topology of an #Azure Application Gateway. In the article, this resource is shown as a shared service managed by a unique Cyber Security team.
High-Level View
Script Workflow
The following chapters enumerate the steps used to build the Application Gateway.
Set up variables
Since these variables are re-used, a local block makes this more maintainable.
project.hcl (this is where basically we define the real values as a variable )
locals {
project_name = "<project_name>"
network_ddos_protection_plan_name = "ddos-leapwork"
zones = ["1", "2", "3"]
}
Now let’s define the files that are basically required to create the application gateway resource,
terragrunt.hcl (which will call the value that we had defined as part of the local variable)
locals {environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
env_name = local.environment_vars.locals.environment
account_vars = read_terragrunt_config(find_in_parent_folders("account.hcl"))
local_dir = local.account_vars.locals.local_dir
azure_account_id = local.account_vars.locals.azure_account_id
azure_account_short_id = local.account_vars.locals.azure_account_short_id
project_vars = read_terragrunt_config(find_in_parent_folders("project.hcl"))
network_ddos_protection_plan_name= local.project_vars.locals.network_ddos_protection_plan_name
project_name = local.project_vars.locals.project_name
region_vars = read_terragrunt_config(find_in_parent_folders("region.hcl"))
azure_region = local.region_vars.locals.azure_region
account_name = local.account_vars.locals.account_name}terraform {
source = "${path_relative_from_include()}"
}
include {
path = find_in_parent_folders()
}
inputs = {
env_name = local.env_name
local_dir = local.local_dir
location = local.azure_region
azure_account_id = local.azure_account_id
azure_account_short_id = local.azure_account_short_id
project_name = local.project_name
network_ddos_protection_plan_name = local.network_ddos_protection_plan_name
account_name = local.account_name
}
variable.tf (this file will populate the values from terragrunt.hcl and will pass it to the resource creation file)
variable "network_ddos_protection_plan_name" {
type = string
description = ""
}
variable "location" {
type = string
description = "The location where the resource group should be created. For a list of all Azure location"
}
variable "env_name" {
type = string
description = "Type of environment ex: dev, stage or prod"
}
variable "local_dir" {
type = string
description = "local account directory name"
}
variable "azure_region" {
type = string
description = "azure location"
}
variable "project_name" {
type = string
description = "project name"
}
variable "account_name" {
type = string
description = "account name"
}
Public IP and Application Gateway
The Static public IP with the Standard SKU is a requirement when using Application Gateway v2 and Availability Zone-aware resources.
The Application Gateway with the upper resources with HTTP to HTTPS redirection.
Note:
Please do check as well how you can access ssl-certificate as a file that is required as part of https listeners creation.
data "terraform_remote_state" "app-gateway-subnet" {
backend = "azurerm"
config = {
resource_group_name = "terraform-state"
storage_account_name = "<your storage account name>"
container_name = "terragruntbackned"
key = "${var.env_name}/${var.local_dir}/${var.location}/${var.account_name}/subnets/terraform.tfstate"
}
}
data "terraform_remote_state" "rg" {
backend = "azurerm"
config = {
resource_group_name = "terraform-state"
storage_account_name = "<your storage account name>"
container_name = "terragruntbackned"
key = "${var.env_name}/${var.local_dir}/${var.location}/${var.account_name}/resource-group/terraform.tfstate"
}
}
data "terraform_remote_state" "controller-vm-nic" {
backend = "azurerm"
config = {
resource_group_name = "terraform-state"
storage_account_name = "<your storage account name>"
container_name = "terragruntbackned"
key = "${var.env_name}/${var.local_dir}/${var.location}/${var.account_name}/vms/controller-vm/terraform.tfstate"
}
}
data "terraform_remote_state" "agent-vm-nic" {
backend = "azurerm"
config = {
resource_group_name = "terraform-state"
storage_account_name = "<your storage account name>"
container_name = "terragruntbackned"
key = "${var.env_name}/${var.local_dir}/${var.location}/${var.account_name}/vms/agent-vm/terraform.tfstate"
}
}
resource "azurerm_public_ip" "app-gateway-public-ip" {
name = "app-gateway-public-ip-${var.project_name}-${var.env_name}"
resource_group_name = data.terraform_remote_state.rg.outputs.rg_name
location = var.location
allocation_method = "Static"
sku = "Standard"
}
resource "azurerm_application_gateway" "app-gateway" {
name = "appgateway-${var.project_name}-${var.env_name}"
resource_group_name = data.terraform_remote_state.rg.outputs.rg_name
location = var.location
sku {
name = "Standard_v2"
tier = "Standard_v2"
}
autoscale_configuration {
min_capacity = 0
max_capacity = 10
}
gateway_ip_configuration {
name = "gateway-ip-configuration-${var.project_name}-${var.env_name}"
subnet_id = data.terraform_remote_state.app-gateway-subnet.outputs.app_gateway_subnet_id
}
frontend_port {
name = "http-frontend-port-${var.project_name}-${var.env_name}"
port = 80
}
frontend_port {
name = "https-frontend-port-${var.project_name}-${var.env_name}"
port = 443
}
frontend_ip_configuration {
name = "frontend-ip-configuration-${var.project_name}-${var.env_name}"
public_ip_address_id = azurerm_public_ip.app-gateway-public-ip.id
}
backend_address_pool {
name = "backend-address-pool-${var.project_name}-${var.env_name}"
}
ssl_certificate {
name = "${var.project_name}-kyvt-${var.env_name}"
data = filebase64("your wild card key")
}
backend_http_settings {
name = "backend-https-setting-${var.project_name}-${var.env_name}"
cookie_based_affinity = "Disabled"
#path = "/"
port = 443
protocol = "Https"
request_timeout = 60
}
backend_http_settings {
name = "backend-http-setting-${var.project_name}-${var.env_name}"
cookie_based_affinity = "Disabled"
#path = "/"
port = 80
protocol = "Http"
request_timeout = 60
}
http_listener {
name = "http-listener-${var.project_name}-${var.env_name}"
frontend_ip_configuration_name = "frontend-ip-configuration-${var.project_name}-${var.env_name}"
frontend_port_name = "http-frontend-port-${var.project_name}-${var.env_name}"
protocol = "Http"
}
http_listener {
name = "https-listener-${var.project_name}-${var.env_name}"
frontend_ip_configuration_name = "frontend-ip-configuration-${var.project_name}-${var.env_name}"
frontend_port_name = "https-frontend-port-${var.project_name}-${var.env_name}"
protocol = "Https"
ssl_certificate_name = "${var.project_name}-kyvt-${var.env_name}"
}
request_routing_rule {
name = "routing-rule-${var.project_name}-${var.env_name}"
rule_type = "Basic"
http_listener_name = "http-listener-${var.project_name}-${var.env_name}"
backend_address_pool_name = "backend-address-pool-${var.project_name}-${var.env_name}"
backend_http_settings_name = "backend-http-setting-${var.project_name}-${var.env_name}"
}
request_routing_rule {
name = "routing-rule-https-${var.project_name}-${var.env_name}"
rule_type = "Basic"
http_listener_name = "https-listener-${var.project_name}-${var.env_name}"
backend_address_pool_name = "backend-address-pool-${var.project_name}-${var.env_name}"
backend_http_settings_name = "backend-https-setting-${var.project_name}-${var.env_name}"
}
}
Allow Application Gateway to point to a specific network interface
The Application Gateway will work as a load balancer for that particular VM which will receive HTTPS requests and send them to the backend as HTTP.
resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "controller_vm_backend_pool" {
network_interface_id = data.terraform_remote_state.controller-vm-nic.outputs.controller_vm_nic_id
ip_configuration_name = data.terraform_remote_state.controller-vm-nic.outputs.controller_vm_ip_configuration_name
backend_address_pool_id = azurerm_application_gateway.app-gateway.backend_address_pool[0].id
}
resource "azurerm_network_interface_application_gateway_backend_address_pool_association" "agent_vm_backend_pool" {
network_interface_id = data.terraform_remote_state.agent-vm-nic.outputs.agent_vm_nic_id
ip_configuration_name = data.terraform_remote_state.agent-vm-nic.outputs.agent_vm_ip_configuration_name
backend_address_pool_id = azurerm_application_gateway.app-gateway.backend_address_pool[0].id
}
To monitor the application gateway, please use the below codes,
resource "azurerm_log_analytics_workspace" "leapwork-wks" {
name = "${var.project_name}-hub-logaw-${var.env_name}"
location = var.location
resource_group_name = data.terraform_remote_state.rg.outputs.rg_name
sku = "PerGB2018" #(Required) Specifies the Sku of the Log Analytics Workspace. Possible values are Free, PerNode, Premium, Standard, Standalone, Unlimited, and PerGB2018 (new Sku as of 2018-04-03).
retention_in_days = 100 #(Optional) The workspace data retention in days. Possible values range between 30 and 730.
#tags = "leapwork-wks"
}
resource "azurerm_log_analytics_solution" "leapwork-agw-solution" {
solution_name = "AzureAppGatewayAnalytics"
location = var.location
resource_group_name = data.terraform_remote_state.rg.outputs.rg_name
workspace_resource_id = azurerm_log_analytics_workspace.leapwork-wks.id
workspace_name = azurerm_log_analytics_workspace.leapwork-wks.name
plan
{
publisher = "Microsoft"
product = "OMSGallery/AzureAppGatewayAnalytics"
}
}