Skip to content

A module used to deploy an Azure Bastion service, with optional subnet creation and NSG with the needed rules 🛡️

License

Notifications You must be signed in to change notification settings

cyber-scot/terraform-azurerm-bastion

Repository files navigation

locals {
  requires_external_subnet = var.create_bastion_subnet == false && var.external_subnet_id == null
}

resource "azurerm_subnet" "bastion_subnet" {
  count                = var.create_bastion_subnet == true ? 1 : 0
  name                 = try(var.bastion_subnet_name, "AzureBastionSubnet") # Must be AzureBastionSubnet
  resource_group_name  = try(var.bastion_subnet_target_vnet_rg_name, null)
  virtual_network_name = try(var.bastion_subnet_target_vnet_name, null)
  address_prefixes     = [var.bastion_subnet_range]

  timeouts {
    create = "5m"
    delete = "10m"
  }
}

resource "azurerm_network_security_group" "bastion_nsg" {
  count               = var.create_bastion_nsg == true ? 1 : 0
  name                = var.bastion_nsg_name != null ? var.bastion_nsg_name : "nsg-${var.bastion_host_name}"
  location            = var.bastion_nsg_location != null ? var.bastion_nsg_location : var.location
  resource_group_name = var.bastion_nsg_rg_name != null ? var.bastion_nsg_rg_name : var.rg_name
  tags                = var.tags

  timeouts {
    create = "5m"
    delete = "10m"
  }
}

// Fix error which causes security errors to be flagged by TFSec, public egress is needed for Azure Bastion to function, its kind of the point :)
#tfsec:ignore:azure-network-no-public-egress[destination_address_prefix="*"]
resource "azurerm_network_security_rule" "bastion_nsg" {
  for_each = var.create_bastion_nsg_rules == true && var.create_bastion_nsg == true ? var.azure_bastion_nsg_list : {}

  name                   = each.key
  priority               = each.value.priority
  direction              = each.value.direction
  access                 = each.value.access
  protocol               = each.value.protocol
  source_port_range      = each.value.source_port
  destination_port_range = each.value.destination_port
  source_address_prefix  = each.value.source_address_prefix

  #tfsec:ignore:azure-network-no-public-egress
  destination_address_prefix = each.value.destination_address_prefix

  resource_group_name         = azurerm_network_security_group.bastion_nsg[0].resource_group_name
  network_security_group_name = azurerm_network_security_group.bastion_nsg[0].name
}

#Fix for https://github.com/terraform-providers/terraform-provider-azurerm/issues/5232
resource "azurerm_subnet_network_security_group_association" "bastion_nsg_association" {
  count      = var.create_bastion_subnet == true && var.create_bastion_nsg == true ? 1 : 0
  depends_on = [azurerm_network_security_rule.bastion_nsg]

  subnet_id                 = azurerm_subnet.bastion_subnet[0].id
  network_security_group_id = azurerm_network_security_group.bastion_nsg[0].id
}

resource "azurerm_public_ip" "bastion_pip" {
  name                = var.bastion_pip_name != null ? var.bastion_pip_name : "pip-${var.bastion_host_name}"
  location            = var.bastion_pip_location != null ? var.bastion_pip_location : var.location
  resource_group_name = var.bastion_pip_rg_name != null ? var.bastion_pip_rg_name : var.rg_name
  allocation_method   = var.bastion_pip_allocation_method
  sku                 = var.bastion_pip_sku
  tags                = var.tags
}

resource "azurerm_bastion_host" "bastion_host" {
  name                   = var.bastion_host_name
  location               = var.location
  resource_group_name    = var.rg_name
  copy_paste_enabled     = var.copy_paste_enabled
  sku                    = title(var.bastion_sku)
  file_copy_enabled      = var.bastion_sku == "Standard" ? var.file_copy_enabled : null
  ip_connect_enabled     = var.bastion_sku == "Standard" ? var.ip_connect_enabled : null
  scale_units            = var.bastion_sku == "Standard" ? var.scale_units : 2 # 2 is default for Basic sku
  shareable_link_enabled = var.bastion_sku == "Standard" ? var.shareable_link_enabled : null
  tunneling_enabled      = var.bastion_sku == "Standard" ? var.tunneling_enabled : null

  dynamic "ip_configuration" {
    for_each = var.create_bastion_subnet || var.external_subnet_id != null ? [1] : []
    content {
      name                 = var.bastion_host_ipconfig_name != null ? var.bastion_host_ipconfig_name : "ipconfig-${var.bastion_host_name}"
      subnet_id            = var.create_bastion_subnet ? azurerm_subnet.bastion_subnet[0].id : var.external_subnet_id
      public_ip_address_id = azurerm_public_ip.bastion_pip.id
    }
  }

  tags = var.tags
}

Requirements

No requirements.

Providers

Name Version
azurerm n/a

Modules

No modules.

Resources

Name Type
azurerm_bastion_host.bastion_host resource
azurerm_network_security_group.bastion_nsg resource
azurerm_network_security_rule.bastion_nsg resource
azurerm_public_ip.bastion_pip resource
azurerm_subnet.bastion_subnet resource
azurerm_subnet_network_security_group_association.bastion_nsg_association resource

Inputs

Name Description Type Default Required
azure_bastion_nsg_list The Standard list of NSG rules needed to make a bastion work map
{
"AllowAzureBastionCommunicationOutbound1": {
"access": "Allow",
"destination_address_prefix": "VirtualNetwork",
"destination_port": "5701",
"direction": "Outbound",
"priority": "180",
"protocol": "Tcp",
"source_address_prefix": "VirtualNetwork",
"source_port": ""
},
"AllowAzureBastionCommunicationOutbound2": {
"access": "Allow",
"destination_address_prefix": "VirtualNetwork",
"destination_port": "8080",
"direction": "Outbound",
"priority": "185",
"protocol": "Tcp",
"source_address_prefix": "VirtualNetwork",
"source_port": "
"
},
"AllowAzureCloudOutbound2": {
"access": "Allow",
"destination_address_prefix": "AzureCloud",
"destination_port": "443",
"direction": "Outbound",
"priority": "170",
"protocol": "Tcp",
"source_address_prefix": "",
"source_port": "
"
},
"AllowAzureLoadBalancerInbound": {
"access": "Allow",
"destination_address_prefix": "",
"destination_port": "443",
"direction": "Inbound",
"priority": "140",
"protocol": "Tcp",
"source_address_prefix": "AzureLoadBalancer",
"source_port": "
"
},
"AllowBastionHostCommunication1": {
"access": "Allow",
"destination_address_prefix": "VirtualNetwork",
"destination_port": "5701",
"direction": "Inbound",
"priority": "150",
"protocol": "Tcp",
"source_address_prefix": "VirtualNetwork",
"source_port": ""
},
"AllowBastionHostCommunication2": {
"access": "Allow",
"destination_address_prefix": "VirtualNetwork",
"destination_port": "80",
"direction": "Inbound",
"priority": "155",
"protocol": "Tcp",
"source_address_prefix": "VirtualNetwork",
"source_port": "
"
},
"AllowGatewayManagerInbound": {
"access": "Allow",
"destination_address_prefix": "",
"destination_port": "443",
"direction": "Inbound",
"priority": "130",
"protocol": "Tcp",
"source_address_prefix": "GatewayManager",
"source_port": "
"
},
"AllowGetSessionInformation": {
"access": "Allow",
"destination_address_prefix": "",
"destination_port": "80",
"direction": "Outbound",
"priority": "190",
"protocol": "Tcp",
"source_address_prefix": "
",
"source_port": ""
},
"AllowHttpsInbound": {
"access": "Allow",
"destination_address_prefix": "
",
"destination_port": "443",
"direction": "Inbound",
"priority": "120",
"protocol": "Tcp",
"source_address_prefix": "Internet",
"source_port": ""
},
"AllowSSHRDPOutbound1": {
"access": "Allow",
"destination_address_prefix": "VirtualNetwork",
"destination_port": "22",
"direction": "Outbound",
"priority": "160",
"protocol": "Tcp",
"source_address_prefix": "
",
"source_port": ""
},
"AllowSSHRDPOutbound2": {
"access": "Allow",
"destination_address_prefix": "VirtualNetwork",
"destination_port": "3389",
"direction": "Outbound",
"priority": "165",
"protocol": "Tcp",
"source_address_prefix": "
",
"source_port": "*"
}
}
no
bastion_host_ipconfig_name The IP Configuration name for the Azure Bastion string null no
bastion_host_name The name for the Bastion host in the portal string n/a yes
bastion_nsg_location The location of the bastion nsg string null no
bastion_nsg_name The name for the NSG to be created with the AzureBastionSubnet string null no
bastion_nsg_rg_name The resource group name which the NSG should be placed in string null no
bastion_pip_allocation_method The allocation method for the Public IP, default is Static string "Static" no
bastion_pip_location The location for the Bastion Public IP, default is UK South string null no
bastion_pip_name The name for the Bastion Public IP string null no
bastion_pip_rg_name The resource group name for Bastion Public IP string null no
bastion_pip_sku The SKU for the Bastion Public IP, default is Standard string "Standard" no
bastion_sku The SKU of the bastion, default is Basic string "Basic" no
bastion_subnet_name The name of the Azure Bastion Subnet - note, this is a static value and should not be changed string "AzureBastionSubnet" no
bastion_subnet_range The IP Range for the Bastion Subnet - Note, Minimum is a /27 string null no
bastion_subnet_target_vnet_name The name of the VNet the bastion is intended to join string null no
bastion_subnet_target_vnet_rg_name The name of the resource group that the VNet can be found in string null no
copy_paste_enabled Whether copy paste is enabled, defaults to true bool true no
create_bastion_nsg Whether a NSG should be created for the Bastion, defaults to true bool true no
create_bastion_nsg_rules Whether the NSG rules for a bastion should be made, default is true bool true no
create_bastion_subnet Whether this module should create the bastion subnet for the user, defaults to true bool true no
external_subnet_id The ID of the external subnet if not created by this module. string null no
file_copy_enabled Whether file copy is enabled bool null no
ip_connect_enabled Whether the IP connect feature is enabled bool null no
location The location for the bastion host, default is UK South string n/a yes
rg_name The resource group name for the Bastion resource string n/a yes
scale_units The number of scale units, default is 2 number 2 no
shareable_link_enabled Whether the shareable link is enabled bool null no
tags The default tags to be assigned map(string) n/a yes
tunneling_enabled Whether the tunneling feature is enable bool null no

Outputs

Name Description
bastion_dns_name The DNS name of the Azure Bastion
bastion_hostname The host name of the bastion
bastion_ip_configuration The bastion host ip_configuration block
bastion_nsg_id The host name of the bastion
bastion_nsg_name The name of the bastion nsg
bastion_subnet_id The subnet ID associated with the bastion host's IP configuration
bastion_subnet_ip_range Bastion subnet IP range

About

A module used to deploy an Azure Bastion service, with optional subnet creation and NSG with the needed rules 🛡️

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published