/terraform-azurerm-network-security-group

Terraform module to create a network security group and assign it to the specified subnet

Primary LanguageHCLMIT LicenseMIT

terraform-azurerm-network-security-group

Notice on Upgrade to V4.x

We've added a CI pipeline for this module to speed up our code review and to enforce a high code quality standard, if you want to contribute by submitting a pull request, please read Pre-Commit & Pr-Check & Test section, or your pull request might be rejected by CI pipeline.

A pull request will be reviewed when it has passed Pre Pull Request Check in the pipeline, and will be merged when it has passed the acceptance tests. Once the CI pipeline failed, please read the pipeline's output, thanks for your cooperation.

V4.0.0 is a major version upgrade. Extreme caution must be taken during the upgrade to avoid resource replacement and downtime by accident.

Running terraform plan first to inspect the plan is strongly advised.

Terraform and terraform-provider-azurerm version restrictions

Now Terraform core's version is v1.x and terraform-provider-azurerm's version is v3.x.

Example Usage

Please refer to the sub folders under examples folder. You can execute terraform apply command in examples's sub folder to try the module. These examples are tested against every PR with the E2E Test.

Create a network security group

This Terraform module deploys a Network Security Group (NSG) in Azure and optionally attach it to the specified vnets.

This module is a complement to the Azure Network module. Use the network_security_group_id from the output of this module to apply it to a subnet in the Azure Network module. NOTE: We are working on adding the support for applying a NSG to a network interface directly as a future enhancement.

This module includes a a set of pre-defined rules for commonly used protocols (for example HTTP or ActiveDirectory) that can be used directly in their corresponding modules or as independent rules.

Usage with the generic module in Terraform 0.13

The following example demonstrate how to use the network-security-group module with a combination of predefined and custom rules.

~> NOTE: source_address_prefix is defined differently in predefined_rules and custom_rules. predefined_rules uses var.source_address_prefix defined in the module.var.source_address_prefix is of type list(string), but allowed only one element (CIDR, *, source IP range or Tags). For more source_address_prefixes, please use var.source_address_prefixes. The same for var.destination_address_prefix in predefined_rules. custom_rules uses source_address_prefix defined in the block custom_rules. source_address_prefix is of type string (CIDR, *, source IP range or Tags). For more source_address_prefixes, please use source_address_prefixes in block custom_rules. The same for destination_address_prefix in custom_rules.

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "my-resources"
  location = "West Europe"
}

module "network-security-group" {
  source                = "Azure/network-security-group/azurerm"
  resource_group_name   = azurerm_resource_group.example.name
  location              = "EastUS" # Optional; if not provided, will use Resource Group location
  security_group_name   = "nsg"
  source_address_prefix = ["10.0.3.0/24"]
  predefined_rules = [
    {
      name     = "SSH"
      priority = "500"
    },
    {
      name              = "LDAP"
      source_port_range = "1024-1026"
    }
  ]

  custom_rules = [
    {
      name                   = "myssh"
      priority               = 201
      direction              = "Inbound"
      access                 = "Allow"
      protocol               = "tcp"
      source_port_range      = "*"
      destination_port_range = "22"
      source_address_prefix  = "10.151.0.0/24"
      description            = "description-myssh"
    },
    {
      name                    = "myhttp"
      priority                = 200
      direction               = "Inbound"
      access                  = "Allow"
      protocol                = "tcp"
      source_port_range       = "*"
      destination_port_range  = "8080"
      source_address_prefixes = ["10.151.0.0/24", "10.151.1.0/24"]
      description             = "description-http"
    },
  ]

  tags = {
    environment = "dev"
    costcenter  = "it"
  }

  depends_on = [azurerm_resource_group.example]
}

Usage with for_each iteration instead of count

~> IMPORTANT NOTES:

var.custom_rules -> name is a mandatory attribute and should be unique across rules.

var.predefined_rules -> priority is a mandatory attribute and should be unique across rules.

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "my-resources"
  location = "West Europe"
}

module "network-security-group" {
  source                = "Azure/network-security-group/azurerm"
  resource_group_name   = azurerm_resource_group.example.name
  location              = "EastUS" # Optional; if not provided, will use Resource Group location
  security_group_name   = "nsg"
  source_address_prefix = ["10.0.3.0/24"]
  use_for_each          = true
  predefined_rules = [
    {
      name     = "SSH"
      priority = "500"
    },
    {
      name              = "LDAP"
      source_port_range = "1024-1026"
    }
  ]

  custom_rules = [
    {
      name                   = "myssh"
      priority               = 201
      direction              = "Inbound"
      access                 = "Allow"
      protocol               = "tcp"
      source_port_range      = "*"
      destination_port_range = "22"
      source_address_prefix  = "10.151.0.0/24"
      description            = "description-myssh"
    },
    {
      name                    = "myhttp"
      priority                = 200
      direction               = "Inbound"
      access                  = "Allow"
      protocol                = "tcp"
      source_port_range       = "*"
      destination_port_range  = "8080"
      source_address_prefixes = ["10.151.0.0/24", "10.151.1.0/24"]
      description             = "description-http"
    },
  ]

  tags = {
    environment = "dev"
    costcenter  = "it"
  }

}

Usage with the generic module in Terraform 0.12

The following example demonstrate how to use the network-security-group module with a combination of predefined and custom rules.

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "my-resources"
  location = "West Europe"
}

module "network-security-group" {
  source                = "Azure/network-security-group/azurerm"
  resource_group_name   = azurerm_resource_group.example.name
  location              = "EastUS" # Optional; if not provided, will use Resource Group location
  security_group_name   = "nsg"
  source_address_prefix = ["10.0.3.0/24"]
  predefined_rules = [
    {
      name     = "SSH"
      priority = "500"
    },
    {
      name              = "LDAP"
      source_port_range = "1024-1026"
    }
  ]

  custom_rules = [
    {
      name                   = "myhttp"
      priority               = "200"
      direction              = "Inbound"
      access                 = "Allow"
      protocol               = "tcp"
      destination_port_range = "8080"
      description            = "description-myhttp"
    }
  ]

  tags = {
    environment = "dev"
    costcenter  = "it"
  }
}

Usage with the Application Security Group module in Terraform 0.12

The following example demonstrate how to use the network-security-group module with a combination of predefined and custom rules with ASG source or destination.

provider "azurerm" {
  features {}
}

resource "azurerm_resource_group" "example" {
  name     = "my-resources"
  location = "West Europe"
}

resource "azurerm_application_security_group" "first" {
  name                = "asg-first"
  location            = "eastus"
  resource_group_name = azurerm_resource_group.example.name
}

resource "azurerm_application_security_group" "second" {
  name                = "asg-second"
  location            = "eastus"
  resource_group_name = azurerm_resource_group.example.name
}

module "network-security-group" {
  source              = "Azure/network-security-group/azurerm"
  resource_group_name = azurerm_resource_group.example.name
  location            = "eastus"
  security_group_name = "nsg"
  predefined_rules = [
    {
      name                                  = "SSH"
      priority                              = "500"
      source_application_security_group_ids = [azurerm_application_security_group.first.id]
    }
  ]

  custom_rules = [
    {
      name                                       = "myhttp"
      priority                                   = "200"
      direction                                  = "Inbound"
      access                                     = "Allow"
      protocol                                   = "tcp"
      destination_port_range                     = "8080"
      description                                = "description-myhttp"
      destination_application_security_group_ids = [azurerm_application_security_group.second.id]
    }
  ]
  tags = {
    environment = "dev"
    costcenter  = "it"
  }
}

Usage with the pre-defined module in Terraform 0.12

The following example demonstrate how to use the pre-defined HTTP module with a custom rule for ssh.

resource "azurerm_resource_group" "example" {
  name     = "my-resources"
  location = "West Europe"
}

module "network-security-group" {
  source              = "Azure/network-security-group/azurerm//examples/HTTP"
  resource_group_name = azurerm_resource_group.example.name
  security_group_name = "nsg"
  custom_rules = [
    {
      name                   = "ssh"
      priority               = "200"
      direction              = "Inbound"
      access                 = "Allow"
      protocol               = "tcp"
      destination_port_range = "22"
      source_address_prefix  = "VirtualNetwork"
      description            = "ssh-for-vm-management"
    }
  ]
  tags = {
    environment = "dev"
    costcenter  = "it"
  }
}

Enable or disable tracing tags

We're using BridgeCrew Yor and yorbox to help manage tags consistently across infrastructure as code (IaC) frameworks. In this module you might see tags like:

resource "azurerm_resource_group" "rg" {
  location = "eastus"
  name     = random_pet.name
  tags = merge(var.tags, (/*<box>*/ (var.tracing_tags_enabled ? { for k, v in /*</box>*/ {
    avm_git_commit           = "3077cc6d0b70e29b6e106b3ab98cee6740c916f6"
    avm_git_file             = "main.tf"
    avm_git_last_modified_at = "2023-05-05 08:57:54"
    avm_git_org              = "lonegunmanb"
    avm_git_repo             = "terraform-yor-tag-test-module"
    avm_yor_trace            = "a0425718-c57d-401c-a7d5-f3d88b2551a4"
  } /*<box>*/ : replace(k, "avm_", var.tracing_tags_prefix) => v } : {}) /*</box>*/))
}

To enable tracing tags, set the variable to true:

module "example" {
  source               = "{module_source}"
  ...
  tracing_tags_enabled = true
}

The tracing_tags_enabled is default to false.

To customize the prefix for your tracing tags, set the tracing_tags_prefix variable value in your Terraform configuration:

module "example" {
  source              = "{module_source}"
  ...
  tracing_tags_prefix = "custom_prefix_"
}

The actual applied tags would be:

{
  custom_prefix_git_commit           = "3077cc6d0b70e29b6e106b3ab98cee6740c916f6"
  custom_prefix_git_file             = "main.tf"
  custom_prefix_git_last_modified_at = "2023-05-05 08:57:54"
  custom_prefix_git_org              = "lonegunmanb"
  custom_prefix_git_repo             = "terraform-yor-tag-test-module"
  custom_prefix_yor_trace            = "a0425718-c57d-401c-a7d5-f3d88b2551a4"
}

Pre-Commit & Pr-Check & Test

Configurations

We assumed that you have setup service principal's credentials in your environment variables like below:

export ARM_SUBSCRIPTION_ID="<azure_subscription_id>"
export ARM_TENANT_ID="<azure_subscription_tenant_id>"
export ARM_CLIENT_ID="<service_principal_appid>"
export ARM_CLIENT_SECRET="<service_principal_password>"

On Windows Powershell:

$env:ARM_SUBSCRIPTION_ID="<azure_subscription_id>"
$env:ARM_TENANT_ID="<azure_subscription_tenant_id>"
$env:ARM_CLIENT_ID="<service_principal_appid>"
$env:ARM_CLIENT_SECRET="<service_principal_password>"

We provide a docker image to run the pre-commit checks and tests for you: mcr.microsoft.com/azterraform:latest

To run the pre-commit task, we can run the following command:

$ docker run --rm -v $(pwd):/src -w /src mcr.microsoft.com/azterraform:latest make pre-commit

On Windows Powershell:

$ docker run --rm -v ${pwd}:/src -w /src mcr.microsoft.com/azterraform:latest make pre-commit

In pre-commit task, we will:

  1. Run terraform fmt -recursive command for your Terraform code.
  2. Run terrafmt fmt -f command for markdown files and go code files to ensure that the Terraform code embedded in these files are well formatted.
  3. Run go mod tidy and go mod vendor for test folder to ensure that all the dependencies have been synced.
  4. Run gofmt for all go code files.
  5. Run gofumpt for all go code files.
  6. Run terraform-docs on README.md file, then run markdown-table-formatter to format markdown tables in README.md.

Then we can run the pr-check task to check whether our code meets our pipeline's requirement (we strongly recommend you run the following command before you commit):

$ docker run --rm -v $(pwd):/src -w /src -e TFLINT_CONFIG=.tflint_alt.hcl mcr.microsoft.com/azterraform:latest make pr-check

On Windows Powershell:

$ docker run --rm -v ${pwd}:/src -w /src -e TFLINT_CONFIG=.tflint_alt.hcl mcr.microsoft.com/azterraform:latest make pr-check

To run the e2e-test, we can run the following command:

docker run --rm -v $(pwd):/src -w /src -e ARM_SUBSCRIPTION_ID -e ARM_TENANT_ID -e ARM_CLIENT_ID -e ARM_CLIENT_SECRET mcr.microsoft.com/azterraform:latest make e2e-test

On Windows Powershell:

docker run --rm -v ${pwd}:/src -w /src -e ARM_SUBSCRIPTION_ID -e ARM_TENANT_ID -e ARM_CLIENT_ID -e ARM_CLIENT_SECRET mcr.microsoft.com/azterraform:latest make e2e-test

Prerequisites

Authors

Originally created by Damien Caro and Richard Guthrie.

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.

License

MIT

Requirements

Name Version
terraform >=1.2
azurerm >=3.11.0, < 4.0

Providers

Name Version
azurerm >=3.11.0, < 4.0

Modules

No modules.

Resources

Name Type
azurerm_network_security_group.nsg resource
azurerm_network_security_rule.custom_rules resource
azurerm_network_security_rule.custom_rules_for resource
azurerm_network_security_rule.predefined_rules resource
azurerm_network_security_rule.predefined_rules_for resource
azurerm_resource_group.nsg data source

Inputs

Name Description Type Default Required
custom_rules Security rules for the network security group using this format name = [name, priority, direction, access, protocol, source_port_range, destination_port_range, source_address_prefix, destination_address_prefix, description] any [] no
destination_address_prefix Destination address prefix to be applied to all predefined rules. list(string) only allowed one element (CIDR, *, source IP range or Tags). Example ["10.0.3.0/24"] or ["VirtualNetwork"] list(string)
[
"*"
]
no
destination_address_prefixes Destination address prefix to be applied to all predefined rules. Example ["10.0.3.0/32","10.0.3.128/32"] list(string) null no
location Location (Azure Region) for the network security group. string "" no
predefined_rules Predefined rules any [] no
resource_group_name Name of the resource group string n/a yes
rules Standard set of predefined rules map(any)
{
"ActiveDirectory-AllowADDSWebServices": [
"Inbound",
"Allow",
"Tcp",
"",
"9389",
"AllowADDSWebServices"
],
"ActiveDirectory-AllowADGCReplication": [
"Inbound",
"Allow",
"Tcp",
"
",
"3268",
"AllowADGCReplication"
],
"ActiveDirectory-AllowADGCReplicationSSL": [
"Inbound",
"Allow",
"Tcp",
"",
"3269",
"AllowADGCReplicationSSL"
],
"ActiveDirectory-AllowADReplication": [
"Inbound",
"Allow",
"
",
"",
"389",
"AllowADReplication"
],
"ActiveDirectory-AllowADReplicationSSL": [
"Inbound",
"Allow",
"
",
"",
"636",
"AllowADReplicationSSL"
],
"ActiveDirectory-AllowADReplicationTrust": [
"Inbound",
"Allow",
"
",
"",
"445",
"AllowADReplicationTrust"
],
"ActiveDirectory-AllowDFSGroupPolicy": [
"Inbound",
"Allow",
"Udp",
"
",
"138",
"AllowDFSGroupPolicy"
],
"ActiveDirectory-AllowDNS": [
"Inbound",
"Allow",
"",
"
",
"53",
"AllowDNS"
],
"ActiveDirectory-AllowFileReplication": [
"Inbound",
"Allow",
"Tcp",
"",
"5722",
"AllowFileReplication"
],
"ActiveDirectory-AllowKerberosAuthentication": [
"Inbound",
"Allow",
"
",
"",
"88",
"AllowKerberosAuthentication"
],
"ActiveDirectory-AllowNETBIOSAuthentication": [
"Inbound",
"Allow",
"Udp",
"
",
"137",
"AllowNETBIOSAuthentication"
],
"ActiveDirectory-AllowNETBIOSReplication": [
"Inbound",
"Allow",
"Tcp",
"",
"139",
"AllowNETBIOSReplication"
],
"ActiveDirectory-AllowPasswordChangeKerberes": [
"Inbound",
"Allow",
"
",
"",
"464",
"AllowPasswordChangeKerberes"
],
"ActiveDirectory-AllowRPCReplication": [
"Inbound",
"Allow",
"Tcp",
"
",
"135",
"AllowRPCReplication"
],
"ActiveDirectory-AllowSMTPReplication": [
"Inbound",
"Allow",
"Tcp",
"",
"25",
"AllowSMTPReplication"
],
"ActiveDirectory-AllowWindowsTime": [
"Inbound",
"Allow",
"Udp",
"
",
"123",
"AllowWindowsTime"
],
"Cassandra": [
"Inbound",
"Allow",
"Tcp",
"",
"9042",
"Cassandra"
],
"Cassandra-JMX": [
"Inbound",
"Allow",
"Tcp",
"
",
"7199",
"Cassandra-JMX"
],
"Cassandra-Thrift": [
"Inbound",
"Allow",
"Tcp",
"",
"9160",
"Cassandra-Thrift"
],
"CouchDB": [
"Inbound",
"Allow",
"Tcp",
"
",
"5984",
"CouchDB"
],
"CouchDB-HTTPS": [
"Inbound",
"Allow",
"Tcp",
"",
"6984",
"CouchDB-HTTPS"
],
"DNS-TCP": [
"Inbound",
"Allow",
"Tcp",
"
",
"53",
"DNS-TCP"
],
"DNS-UDP": [
"Inbound",
"Allow",
"Udp",
"",
"53",
"DNS-UDP"
],
"DynamicPorts": [
"Inbound",
"Allow",
"Tcp",
"
",
"49152-65535",
"DynamicPorts"
],
"ElasticSearch": [
"Inbound",
"Allow",
"Tcp",
"",
"9200-9300",
"ElasticSearch"
],
"FTP": [
"Inbound",
"Allow",
"Tcp",
"
",
"21",
"FTP"
],
"HTTP": [
"Inbound",
"Allow",
"Tcp",
"",
"80",
"HTTP"
],
"HTTPS": [
"Inbound",
"Allow",
"Tcp",
"
",
"443",
"HTTPS"
],
"IMAP": [
"Inbound",
"Allow",
"Tcp",
"",
"143",
"IMAP"
],
"IMAPS": [
"Inbound",
"Allow",
"Tcp",
"
",
"993",
"IMAPS"
],
"Kestrel": [
"Inbound",
"Allow",
"Tcp",
"",
"22133",
"Kestrel"
],
"LDAP": [
"Inbound",
"Allow",
"Tcp",
"
",
"389",
"LDAP"
],
"MSSQL": [
"Inbound",
"Allow",
"Tcp",
"",
"1433",
"MSSQL"
],
"Memcached": [
"Inbound",
"Allow",
"Tcp",
"
",
"11211",
"Memcached"
],
"MongoDB": [
"Inbound",
"Allow",
"Tcp",
"",
"27017",
"MongoDB"
],
"MySQL": [
"Inbound",
"Allow",
"Tcp",
"
",
"3306",
"MySQL"
],
"Neo4J": [
"Inbound",
"Allow",
"Tcp",
"",
"7474",
"Neo4J"
],
"POP3": [
"Inbound",
"Allow",
"Tcp",
"
",
"110",
"POP3"
],
"POP3S": [
"Inbound",
"Allow",
"Tcp",
"",
"995",
"POP3S"
],
"PostgreSQL": [
"Inbound",
"Allow",
"Tcp",
"
",
"5432",
"PostgreSQL"
],
"RDP": [
"Inbound",
"Allow",
"Tcp",
"",
"3389",
"RDP"
],
"RabbitMQ": [
"Inbound",
"Allow",
"Tcp",
"
",
"5672",
"RabbitMQ"
],
"Redis": [
"Inbound",
"Allow",
"Tcp",
"",
"6379",
"Redis"
],
"Riak": [
"Inbound",
"Allow",
"Tcp",
"
",
"8093",
"Riak"
],
"Riak-JMX": [
"Inbound",
"Allow",
"Tcp",
"",
"8985",
"Riak-JMX"
],
"SMTP": [
"Inbound",
"Allow",
"Tcp",
"
",
"25",
"SMTP"
],
"SMTPS": [
"Inbound",
"Allow",
"Tcp",
"",
"465",
"SMTPS"
],
"SSH": [
"Inbound",
"Allow",
"Tcp",
"
",
"22",
"SSH"
],
"WinRM": [
"Inbound",
"Allow",
"Tcp",
"*",
"5986",
"WinRM"
]
}
no
security_group_name Network security group name string "nsg" no
source_address_prefix Source address prefix to be applied to all predefined rules. list(string) only allowed one element (CIDR, *, source IP range or Tags). Example ["10.0.3.0/24"] or ["VirtualNetwork"] list(string)
[
"*"
]
no
source_address_prefixes Destination address prefix to be applied to all predefined rules. Example ["10.0.3.0/32","10.0.3.128/32"] list(string) null no
tags The tags to associate with your network security group. map(string) {} no
tracing_tags_enabled Whether enable tracing tags that generated by BridgeCrew Yor. bool false no
tracing_tags_prefix Default prefix for generated tracing tags string "avm_" no
use_for_each Choose wheter to use 'for_each' as iteration technic to generate the rules, defaults to false so we will use 'count' for compatibilty with previous module versions, but prefered method is 'for_each' bool false no

Outputs

Name Description
network_security_group_id The id of newly created network security group
network_security_group_name The name of newly created network security group