avatarGuillermo Musumeci

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

34344

Abstract

oup.network-rg.location resource_group_name = azurerm_resource_group.network-rg.name }</pre></div><p id="1ef6">And then, we will add the <b>Inbound rules</b> required. In the code above, we will create a security rule for EACH domain controller:</p><div id="39ab"><pre><span class="hljs-comment">## Inbound Rules ##</span>

<span class="hljs-comment"># Port 53 DNS UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_53_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">150</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"53"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 88 Kerberos TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_88_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">160</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"88"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 135 RPC TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_135_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">170</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"135"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 389 LDAP TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_389_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">180</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"389"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 445 SMB TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_445_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">190</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"445"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 49152-65535 TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_49152-65535_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 49152-65535 TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 49152-65535 TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">200</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"49152-65535"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 49152-65535 UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_49152-65535_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 49152-65535 UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 49152-65535 UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">210</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"49152-65535"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Allow ping AD Domain Controllers</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"icmp_client_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD Ping to DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD Ping to DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">220</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Icmp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">""</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">"*"</span> }</pre></div><p id="93fb">After that, we will add the <b>Outbound rules</b> required for EACH domain controller:</p><div id="7615"><pre><span class="hljs-comment">## Outbound Rules ##</span>

<span class="hljs-comment"># Port 53 DNS UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_53_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">150</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"53"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 88 Kerberos TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_88_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">160</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"88"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 135 RPC TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_135_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">170</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"135"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 389 LDAP TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_389_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">180</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"389"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 445 SMB TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_445_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">190</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"445"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 49152-65535 TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_49152-65535_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 49152-65535 TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 49152-65535 TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">200</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"49152-65535"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 49152-65535 UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_49152-65535_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 49152-65535 UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 49152-65535 UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">210</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"49152-65535"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Allow ping AD Domain Controllers</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"icmp_client_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD Ping to DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD Ping to DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">220</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Icmp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">""</span> source_address_prefix = <span class="hljs-string">"*"</span> destination_address_prefix = local.dns_servers[count.index] }</pre></div><h1 id="e8b6">19. Creating an NSG (Network Security Group) for Communication Between AD Domain Controllers</h1><p id="7161">We must open many ports to communicate between AD Domain Controllers, particularly if our machines are located in different subnets, VNETs, or on-prem. However, if both AD domain controllers are located on the same subnet, this NSG is not required.</p><p id="a807">We will start creating the NSG for the client-to-DC communication:</p><div id="d3cf"><pre><span class="hljs-comment"># Create the security group for AD Domain Controllers</span> resource <span class="hljs-string">"azurerm_network_security_group"</span> <span class="hljs-string">"active-directory-dc-nsg"</span> { name = <span class="hljs-string">"active-directory-dc-nsg"</span> location = azurerm_resource_group.network-rg.location resource_group_name = azurerm_resource_group.network-rg.name }</pre></div><p id="a7ce">And then, we will add the <b>Inbound rules</b> required. In the code above, we will create a security rule for EACH domain controller:</p><div id="19cb"><pre><span class="hljs-comment">## Inbound Rules ##</span>

<span class="hljs-comment"># Port 53 DNS TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_53_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 53 DNS TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 53 DNS TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">100</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"53"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 53 DNS UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_53_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">110</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"53"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 88 Kerberos TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_88_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">120</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"88"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 88 Kerberos UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_88_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 88 Kerberos UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 88 Kerberos UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">130</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"88"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 123 W32Time UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_123_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 123 W32Time UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 123 W32Time UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">140</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"123"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 135 RPC TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_135_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">150</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"135"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 137-138 NetLogon UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_137-138_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 137-138 NetLogon UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 137-138 NetLogon UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">160</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"137-138"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 139 NetLogon TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_139_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 139 NetLogon TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 139 NetLogon TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">170</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"139"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 389 LDAP TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_389_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">180</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"389"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 389 LDAP UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span

Options

<span class="hljs-string">"udp_389_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 389 LDAP UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 389 LDAP UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">190</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"389"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 445 SMB TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_445_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">200</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"445"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 464 Kerberos Authentication TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_464_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 464 Kerberos Authentication TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 464 Kerberos Authentication TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">210</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"464"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 464 Kerberos Authentication UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_464_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 464 Kerberos Authentication UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 464 Kerberos Authentication UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">220</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"464"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 636 LDAP SSL TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_636_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 636 LDAP SSL TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 636 LDAP SSL TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">230</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"636"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 3268-3269 LDAP GC TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_3268-3269_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 3268-3269 LDAP GC TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 3268-3269 LDAP GC TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">240</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"3268-3269"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 49152-65535 TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_49152-65535_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 49152-65535 TCP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 49152-65535 TCP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">250</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"49152-65535"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Port 49152-65535 UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_49152-65535_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 49152-65535 UDP - DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD 49152-65535 UDP - DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">260</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"49152-65535"</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">""</span> }

<span class="hljs-comment"># Allow ping AD Domain Controllers</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"icmp_dc_inbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD Ping to DC{count.index+1} Inbound"</span> description = <span class="hljs-string">"AD Ping to DC{count.index+1} Inbound"</span> priority = (<span class="hljs-number">270</span> + count.index) direction = <span class="hljs-string">"Inbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Icmp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">""</span> source_address_prefix = local.dns_servers[count.index] destination_address_prefix = <span class="hljs-string">"*"</span> }</pre></div><p id="40ab">After that, we will add the <b>Outbound rules</b> required for EACH domain controller:</p><div id="5fb5"><pre><span class="hljs-comment">## Outbound Rules ##</span>

<span class="hljs-comment"># Port 53 DNS TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_53_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 53 DNS TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 53 DNS TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">100</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"53"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 53 DNS UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_53_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 53 DNS UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">110</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"53"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 88 Kerberos TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_88_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 88 Kerberos TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">120</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"88"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 88 Kerberos UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_88_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 88 Kerberos UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 88 Kerberos UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">130</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"88"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 123 W32Time UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_123_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 123 W32Time UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 123 W32Time UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">140</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"123"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 135 RPC TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_135_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 135 RPC TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">150</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"135"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 137-138 NetLogon UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_137-138_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 137-138 NetLogon UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 137-138 NetLogon UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">160</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"137-138"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 139 NetLogon TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_139_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 139 NetLogon TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 139 NetLogon TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">170</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"139"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 389 LDAP TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_389_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 389 LDAP TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">180</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"389"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 389 LDAP UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_389_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 389 LDAP UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 389 LDAP UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">190</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"389"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 445 SMB TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_445_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 445 SMB TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">200</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"445"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 464 Kerberos Authentication TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_464_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 464 Kerberos Authentication TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 464 Kerberos Authentication TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">210</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"464"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 464 Kerberos Authentication UDP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"udp_464_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 464 Kerberos Authentication UDP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 464 Kerberos Authentication UDP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">220</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Udp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"464"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] }

<span class="hljs-comment"># Port 636 LDAP SSL TCP</span> resource <span class="hljs-string">"azurerm_network_security_rule"</span> <span class="hljs-string">"tcp_636_dc_outbound"</span> { depends_on = [azurerm_resource_group.network-rg]

count = <span class="hljs-title function_ invoke__">length</span>(local.dns_servers)

network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name resource_group_name = azurerm_resource_group.network-rg.name name = <span class="hljs-string">"AD 636 LDAP SSL TCP - DC{count.index+1} Outbound"</span> description = <span class="hljs-string">"AD 636 LDAP SSL TCP - DC{count.index+1} Outbound"</span> priority = (<span class="hljs-number">230</span> + count.index) direction = <span class="hljs-string">"Outbound"</span> access = <span class="hljs-string">"Allow"</span> protocol = <span class="hljs-string">"Tcp"</span> source_port_range = <span class="hljs-string">""</span> destination_port_range = <span class="hljs-string">"636"</span> source_address_prefix = <span class="hljs-string">""</span> destination_address_prefix = local.dns_servers[count.index] <span class="hljs-number">8</span>. Creating the Input Definition Variables File In the last step, we will create the input definition variables file “terraform.tfvars” <span class="hljs-keyword">and</span> add the following code. The input variables let us customize aspects of Terraform modules without altering the source code.

<span class="hljs-comment"># Common Variables #</span> location = <span class="hljs-string">"northeurope"</span>

<span class="hljs-comment"># Authentication #</span> azure-tenant-id = <span class="hljs-string">"complete-this"</span> azure-subscription-id = <span class="hljs-string">"complete-this"</span> azure-client-id = <span class="hljs-string">"complete-this"</span> azure-client-secret = <span class="hljs-string">"complete-this"</span>

<span class="hljs-comment"># Network #</span> network-vnet-cidr = <span class="hljs-string">"10.128.0.0/16"</span> network-vm-subnet-cidr = <span class="hljs-string">"10.128.1.0/24"</span> network-bastion-subnet-cidr = <span class="hljs-string">"10.128.2.0/24"</span>}</pre></div><h1 id="e7c0">20. Creating the Input Definition Variables File</h1><p id="8ba2">In the last step, we will create the input definition variables file “<b>terraform.tfvars”</b> and add the following code. The input variables let us customize aspects of Terraform modules without altering the source code.</p><div id="bd62"><pre><span class="hljs-comment"># Common Variables #</span> location = <span class="hljs-string">"northeurope"</span>

<span class="hljs-comment"># Authentication #</span> azure-tenant-id = <span class="hljs-string">"complete-this"</span> azure-subscription-id = <span class="hljs-string">"complete-this"</span> azure-client-id = <span class="hljs-string">"complete-this"</span> azure-client-secret = <span class="hljs-string">"complete-this"</span>

<span class="hljs-comment"># Network #</span> network-vnet-cidr = <span class="hljs-string">"10.129.0.0/16"</span> network-vm-subnet-cidr = <span class="hljs-string">"10.129.1.0/24"</span>

<span class="hljs-comment"># Active Directory #</span> ad_domain_name = <span class="hljs-string">"kopicloud.local"</span> ad_domain_netbios_name = <span class="hljs-string">"kopicloud"</span> ad_admin_username = <span class="hljs-string">"kopiadmin"</span> ad_admin_password = <span class="hljs-string">"L30M3ss110"</span> ad_safe_mode_administrator_password = <span class="hljs-string">"R3c0v3ryAcc3ssM0d3"</span>

<span class="hljs-comment"># Domain Controllers #</span> ad_dc1_name = <span class="hljs-string">"kopi-dev-dc1"</span> ad_dc1_ip_address = <span class="hljs-string">"10.129.1.11"</span> dc1_vm_size = <span class="hljs-string">"Standard_B2s"</span>

ad_dc2_name = <span class="hljs-string">"kopi-dev-dc2"</span> ad_dc2_ip_address = <span class="hljs-string">"10.129.1.12"</span> dc2_vm_size = <span class="hljs-string">"Standard_B2s"</span></pre></div><h1 id="15b5">21. Deployment Notes:</h1><p id="9ea7">To deploy the code:</p><ol><li>Clone the repo to your computer</li><li>Move the files “<b>vm-dc2-main.tf”</b> and <b>“vm-dc2-output.tf”</b> outside the folder</li><li>Execute <b>“terraform init”</b></li><li>Execute <b>“terraform apply”</b></li><li>When execution is complete and the DC1 is running, copy the files <b>“vm-dc2-main.tf “</b>, and <b>“vm-dc2-output.tf”</b> back to the folder</li><li>Execute <b>“terraform apply”</b> to create the DC2</li></ol><blockquote id="70bf"><p><b>Note:</b> if you execute both DC1 and DC2 together, DC2 will fail to join the domain, as the setup of the AD domain takes several minutes.</p></blockquote><p id="48b0">The complete code to deploy the <b>Active Directory (AD) Domain Controller (DC) Virtual Machine (VM) in Azure with Terraform </b>is here → <a href="https://github.com/KopiCloud/terraform-azure-active-directory-dc-vm">https://github.com/KopiCloud/terraform-azure-active-directory-dc-vm</a></p><p id="9443">And that’s all, folks. If you liked this story, please show your support by 👏 for this story. Thank you for reading!</p></article></body>

How to Deploy Active Directory (AD) Domain Controller (DC) Virtual Machine (VM) in Azure with Terraform

Thousands of companies worldwide rely on Active Directory for user authentication; these companies commonly implement a few domain controllers in Azure, running on a Virtual Machine (VM) to authenticate the user and applications running on the cloud.

In this story, we will deploy two AD DCs. The first one, DC1, will create a new AD Domain Forest in the cloud, and the second one, DC2, will join this new AD Domain.

These examples will help you to create different scenarios, such as AD Domain on the cloud, hybrid AD deployments when you join existing AD on-premises, create test environments on the cloud, etc.

The code can be used to deploy one or more AD DCs, with a little code editing.

Note: for hybrid scenarios, make sure that you set the on-premises DNS on the NIC of new VMs and that there are network routes in place, or the machines will not be able to join the existing AD Domain.

Active Directory in Cloud Environments

This story is part of my Active Directory in Cloud Environments series.

I have been deploying Active Directory in AWS, Azure, GCP, and OCI cloud environments for +10 years. I have been using AD since Microsoft launched the public beta in 1999, so this is one of my favorite subjects to write about.

1. Requirements

To deploy AD DCs in Azure VMs, we will need the following:

  • Define the Azure Provider
  • Create a Resource Group
  • Create a VNET
  • Create a Subnet
  • Create NSG (Network Security Group) for Client Machines to AD Domain Controllers.
  • Create NSG (Network Security Group) for Communications between Domain Controllers.
  • Create a NIC (Network Card) in this Subnet
  • Create the Virtual Machine to Create a New AD Forest and Domain
  • Create the Virtual Machine to Join an Existing Domain

2. Prerequisites

Before creating our Azure Virtual Machine, we will need an Azure SPN (Service Principal) to execute our Terraform code (check step 1 of this story if you need help creating an SPN).

3. Defining the Azure Provider

First, we will define Azure authentication variables.

We will use a Service Principal with a Client Secret. Check the link below for more info about Azure authentication for Terraform: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret

variable "azure_subscription_id" {
  type        = string
  description = "Azure Subscription ID"
}

variable "azure_client_id" {
  type        = string
  description = "Azure Client ID"
}

variable "azure_client_secret" {
  type        = string
  description = "Azure Client Secret"
}

variable "azure_tenant_id" {
  type        = string
  description = "Azure Tenant ID"
}

Then, we will configure Terraform and the Azure provider:

# Define Terraform provider
terraform {
  required_version = "~> 1.3"
}

# Configure the Azure provider
provider "azurerm" { 
  features {}  
  environment     = "public"
  subscription_id = var.azure-subscription-id
  client_id       = var.azure-client-id
  client_secret   = var.azure-client-secret
  tenant_id       = var.azure-tenant-id
}

4. Creating a Resource Group

In this step, we will create an Azure Resource Group, where we will store all the Azure resources created by our Terraform code.

We will start defining the location variable:

# azure region variable
variable "location" {
  type        = string
  description = "Azure region for the resource group"
  default     = "north europe"
}

And then the code to create the resource group:

resource "azurerm_resource_group" "network-rg" {
  name     = "network-rg"
  location = var.location
}

5. Creating a VNET and Subnet

Now, we will create the variables to configure the network:

variable "network-vnet-cidr" {
  type        = string
  description = "The CIDR of the network VNET"
}

variable "network-subnet-cidr" {
  type        = string
  description = "The CIDR for the network subnet"
}

And then the code to create the VNET and the Subnet:

# Create the network VNET
resource "azurerm_virtual_network" "network-vnet" {
  name = "network-vnet"
  address_space = [var.network-vnet-cidr]
  resource_group_name = azurerm_resource_group.network-rg.name
  location = azurerm_resource_group.network-rg.location
}

# Create a subnet for VM
resource "azurerm_subnet" "ad-subnet" {
  name = "vm-subnet"
  address_prefixes = [var.ad-subnet-cidr]
  virtual_network_name = azurerm_virtual_network.network-vnet.name
  resource_group_name  = azurerm_resource_group.network-rg.name
}

6. Defining Active Directory Variables

In this step, we will define variables used to configure the Active Directory.

# active directory domain name
variable "ad_domain_name" {
  type        = string
  description = "This variable defines the name of Active Directory domain, for example kopicloud.local"
}

# active directory domain NetBIOS name
variable "ad_domain_netbios_name" {
  type        = string
  description = "This variable defines the NETBIOS name of Active Directory domain, for example kopicloud"
}

# active directory domain mode
variable "ad_domain_mode" {
  type        = string
  description = "This variable defines the mode of Active Directory domain and forest functional level"
  default     = "WinThreshold" # Windows Server 2016
}

# local administrator username
variable "ad_admin_username" {
  type        = string
  description = "The username associated with the local administrator account on the virtual machine"
}

# local administrator password
variable "ad_admin_password" {
  type        = string
  description = "The password associated with the local administrator account on the virtual machine"
}

# active directory database path
variable "ad_database_path" {
  type        = string
  description = "The active directory database path"
  default     = "C:/Windows/NTDS"
}

# active directory sysvol path
variable "ad_sysvol_path" {
  type        = string
  description = "The active directory SYSVOL path"
  default     = "C:/Windows/SYSVOL"
}

# active directory log path
variable "ad_log_path" {
  type        = string
  description = "The active directory log path"
  default     = "C:/Windows/NTDS"
}

# active directory safe mode administrator password
variable "ad_safe_mode_administrator_password" {
  type        = string
  description = "The active directory safe mode administrator password"
}

7. Defining Operating System Variables

Here, we will define the Windows_xxxx_SKU variable used to create the AD DC VMs.

# Windows Server 2022 SKU used to build VMs
variable "windows_2022_sku" {
  type        = string
  description = "Windows Server 2022 SKU used to build VMs"
  default     = "2022-Datacenter"
}

# Windows Server 2019 SKU used to build VMs
variable "windows_2019_sku" {
  type        = string
  description = "Windows Server 2019 SKU used to build VMs"
  default     = "2019-Datacenter"
}

# Windows Server 2016 SKU used to build VMs
variable "windows_2016_sku" {
  type        = string
  description = "Windows Server 2016 SKU used to build VMs"
  default     = "2016-Datacenter"
}

8. Create an Availability Set for Domain Controllers

And now, we will create an Availability Set for Virtual Machines so Azure can deploy the AD DCs in different racks in their data centers.

resource "azurerm_availability_set" "dc-availability-set" {
  name                         = "dc-availability-set"
  resource_group_name          = azurerm_resource_group.network-rg.name
  location                     = azurerm_resource_group.network-rg.location
  platform_fault_domain_count  = 3
  platform_update_domain_count = 5
  managed                      = true
}

9. Defining Variables for AD DC1 VM

In this step, we will define the variables we will use to deploy the first VM.

# Azure virtual machine settings #

variable "dc1_vm_size" {
  type        = string
  description = "Size (SKU) of the virtual machine to create"
  default     = "Standard_B2s"
}

variable "dc1_license_type" {
  type        = string
  description = "Specifies the BYOL type for the virtual machine. Possible values are 'Windows_Client' and 'Windows_Server' if set"
  default     = null
}

# Azure virtual machine storage settings #

variable "dc1_delete_os_disk_on_termination" {
  type        = string
  description = "Should the OS Disk (either the Managed Disk / VHD Blob) be deleted when the Virtual Machine is destroyed?"
  default     = "true"  # Update for your environment
}

variable "dc1_delete_data_disks_on_termination" {
  description = "Should the Data Disks (either the Managed Disks / VHD Blobs) be deleted when the Virtual Machine is destroyed?"
  type        = string
  default     = "true" # Update for your environment
}

# Active Directory Configuration #

# domain controller 1 name
variable "ad_dc1_name" {
  type        = string
  description = "This variable defines the name of AD Domain Controller 1"
}

# domain controller 1 private ip address
variable "ad_dc1_ip_address" {
  type        = string
  description = "This variable defines the private ip address of AD Domain Controller 1"
}

10. Creating Variables for AD DC VMs

In this step, we will create two local variables for configuring NICs and bootstrapping scripts.

  • The dns_servers variable contains the list of DNS servers. In this example, we use the IPs of the two domain controllers. However, if you need to join the on-premises AD domain, you may need to update this variable.
  • The dc_servers variable contains the list of AD DC servers. In this example, we use the IPs of the two domain controllers. You should use more or fewer domain controllers in your environment.
locals {
  dns_servers = [ var.ad_dc1_ip_address, var.ad_dc2_ip_address ]
  dc_servers = [ var.ad_dc1_ip_address, var.ad_dc2_ip_address ]
}

11. Creating the NIC for AD DC1 VM

Here, we will create the NIC for the first domain controller. There are two important points here:

  • Assigning a Static IP address to the Domain Controller
  • Configuring the DNS server properly
resource "azurerm_network_interface" "dc1-nic" {
  name                    = "${var.ad_dc1_name}-nic"
  location                = azurerm_resource_group.network-rg.location
  resource_group_name     = azurerm_resource_group.network-rg.name
  internal_dns_name_label = var.ad_dc1_name
  dns_servers             = local.dns_servers

  ip_configuration {
    name                          = "${var.ad_dc1_name}-ip-config"
    subnet_id                     = azurerm_subnet.network-subnet.id
    private_ip_address_allocation = "Static"
    private_ip_address            = var.ad_dc1_ip_address
  }
}

12. Creating the Virtual Machine for AD DC1 VM

In this code, we will launch a Windows Server 2022 virtual machine. However, we can change the operating system to Windows 2019 or 2016 using the SKU variables defined in step #7.

# DC1 virtual machine
resource "azurerm_windows_virtual_machine" "dc1-vm" {
 
  name                = "${var.ad_dc1_name}-vm"
  computer_name       = "${var.ad_dc1_name}-vm"
  location            = azurerm_resource_group.network-rg.location
  resource_group_name = azurerm_resource_group.network-rg.name
  availability_set_id = azurerm_availability_set.dc-availability-set.id
  
  size           = var.dc1_vm_size
  admin_username = var.ad_admin_username
  admin_password = var.ad_admin_password
  license_type   = var.dc1_license_type

  network_interface_ids = [azurerm_network_interface.dc1-nic.id]

  os_disk {
    name                 = "${var.ad_dc1_name}-vm-os-disk"
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = var.windows_2022_sku
    version   = "latest"
  }

  enable_automatic_updates = true
  provision_vm_agent       = true
}

13. Installing Active Directory in AD DC1 VM and Creating an AD Domain Controller for a New AD Forest and Domain

Finally, we will install the Active Directory on our virtual machine using PowerShell.

This code creates an AD domain controller for a new AD Forest and Domain.

First, we will define the inline script to install the prerequisites and set up the domain controller.

# Local variables for DC1
locals {
  dc1_fqdn = "${var.ad_dc1_name}.${var.ad_domain_name}"
  
  dc1_prereq_ad_1 = "Import-Module ServerManager"
  dc1_prereq_ad_2 = "Install-WindowsFeature AD-Domain-Services -IncludeAllSubFeature -IncludeManagementTools"
  dc1_prereq_ad_3 = "Install-WindowsFeature DNS -IncludeAllSubFeature -IncludeManagementTools"
  dc1_prereq_ad_4 = "Import-Module ADDSDeployment"
  dc1_prereq_ad_5 = "Import-Module DnsServer"

  dc1_install_ad_1         = "Install-ADDSForest -DomainName ${var.ad_domain_name} -DomainNetbiosName ${var.ad_domain_netbios_name} -DomainMode ${var.ad_domain_mode} -ForestMode ${var.ad_domain_mode} "
  dc1_install_ad_2         = "-DatabasePath ${var.ad_database_path} -SysvolPath ${var.ad_sysvol_path} -LogPath ${var.ad_log_path} -NoRebootOnCompletion:$false -Force:$true "
  dc1_install_ad_3         = "-SafeModeAdministratorPassword (ConvertTo-SecureString ${var.ad_safe_mode_administrator_password} -AsPlainText -Force)"
  
  dc1_shutdown_command     = "shutdown -r -t 10"
  dc1_exit_code_hack       = "exit 0"
  dc1_powershell_command   = "${local.dc1_prereq_ad_1}; ${local.dc1_prereq_ad_2}; ${local.dc1_prereq_ad_3}; ${local.dc1_prereq_ad_4}; ${local.dc1_prereq_ad_5}; ${local.dc1_install_ad_1}${local.dc1_install_ad_2}${local.dc1_install_ad_3}; ${local.dc1_shutdown_command}; ${local.dc1_exit_code_hack}"
}

And then, we will use a virtual machine extension to install and configure AD. The “CustomScriptExtension” will execute the inline PowerShell defined above.

resource "azurerm_virtual_machine_extension" "dc1-vm-extension" {
  depends_on=[azurerm_windows_virtual_machine.dc1-vm]

  name                 = "${var.ad_dc1_name}-vm-active-directory"
  virtual_machine_id   = azurerm_windows_virtual_machine.dc1-vm.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"  
  settings = <<SETTINGS
  {
    "commandToExecute": "powershell.exe -Command \"${local.dc1_powershell_command}\""
  }
  SETTINGS
}

14. Defining Variables for AD DC2 VM

In this step, we will define the variables we will use to deploy the second VM.

# Azure virtual machine settings #

variable "dc2_vm_size" {
  type        = string
  description = "Size (SKU) of the virtual machine to create"
  default     = "Standard_B2s"
}

variable "dc2_license_type" {
  type        = string
  description = "Specifies the BYOL type for the virtual machine. Possible values are 'Windows_Client' and 'Windows_Server' if set"
  default     = null
}

# Azure virtual machine storage settings #

variable "dc2_delete_os_disk_on_termination" {
  type        = string
  description = "Should the OS Disk (either the Managed Disk / VHD Blob) be deleted when the Virtual Machine is destroyed?"
  default     = "true"  # Update for your environment
}

variable "dc2_delete_data_disks_on_termination" {
  description = "Should the Data Disks (either the Managed Disks / VHD Blobs) be deleted when the Virtual Machine is destroyed?"
  type        = string
  default     = "true" # Update for your environment
}

# Active Directory Configuration #

# domain controller 2name
variable "ad_dc2_name" {
  type        = string
  description = "This variable defines the name of AD Domain Controller 2"
}

# domain controller 2private ip address
variable "ad_dc2_ip_address" {
  type        = string
  description = "This variable defines the private IP address of AD Domain Controller 2"
}

15. Creating the NIC for AD DC2 VM

Here, we will create the NIC for the second domain controller. There are two important points here:

  • Assigning a Static IP address to the Domain Controller
  • Configuring the DNS server properly, in particular, as the right DNS server is required to join the server to the existing AD Domain
resource "azurerm_network_interface" "dc2-nic" {
  name                    = "${var.ad_dc2_name}-nic"
  location                = azurerm_resource_group.network-rg.location
  resource_group_name     = azurerm_resource_group.network-rg.name
  internal_dns_name_label = var.ad_dc2_name
  dns_servers             = local.dns_servers

  ip_configuration {
    name                          = "${var.ad_dc2_name}-ip-config"
    subnet_id                     = azurerm_subnet.network-subnet.id
    private_ip_address_allocation = "Static"
    private_ip_address            = var.ad_dc2_ip_address
  }
}

16. Creating the Virtual Machine for AD DC2 VM

In this code, we will launch a Windows Server 2022 virtual machine. However, we can change the operating system to Windows 2019 or 2016 using the SKU variables defined in step #7.

# DC2 virtual machine
resource "azurerm_windows_virtual_machine" "dc2-vm" {
 
  name                = "${var.ad_dc2_name}-vm"
  computer_name       = "${var.ad_dc2_name}-vm"
  location            = azurerm_resource_group.network-rg.location
  resource_group_name = azurerm_resource_group.network-rg.name
  availability_set_id = azurerm_availability_set.dc-availability-set.id
  
  size           = var.dc2_vm_size
  admin_username = var.ad_admin_username
  admin_password = var.ad_admin_password
  license_type   = var.dc2_license_type

  network_interface_ids = [azurerm_network_interface.dc2-nic.id]

  os_disk {
    name                 = "${var.ad_dc2_name}-vm-os-disk"
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = var.windows_2022_sku
    version   = "latest"
  }

  enable_automatic_updates = true
  provision_vm_agent       = true
}

17. Installing Active Directory in AD DC2 VM and Joining an Existing AD Domain

Finally, we will install the Active Directory on our virtual machine using PowerShell.

This code joins the AD DC2 VM to an existing AD Domain.

First, we will define the inline script to install the prerequisites, configure credentials to join the domain, and set up the domain controller.

# Local variables for DC2
locals {
  dc2_fqdn = "${var.ad_dc2_name}.${var.ad_domain_name}"

  dc2_prereq_ad_1 = "Import-Module ServerManager"
  dc2_prereq_ad_2 = "Install-WindowsFeature AD-Domain-Services -IncludeAllSubFeature -IncludeManagementTools"
  dc2_prereq_ad_3 = "Install-WindowsFeature DNS -IncludeAllSubFeature -IncludeManagementTools"
  dc2_prereq_ad_4 = "Import-Module ADDSDeployment"
  dc2_prereq_ad_5 = "Import-Module DnsServer"

  dc2_credentials_1 = "$User = '${var.ad_admin_username}@${var.ad_domain_name}'"
  dc2_credentials_2 = "$PWord = ConvertTo-SecureString -String ${var.ad_admin_password} -AsPlainText -Force"
  dc2_credentials_3 = "$Credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $User, $PWord"

  dc2_install_ad_1 = "Install-ADDSDomainController -DomainName ${var.ad_domain_name} -Credential $Credential -InstallDns:$true -CreateDnsDelegation:$false "
  dc2_install_ad_2 = "-DatabasePath ${var.ad_database_path} -SysvolPath ${var.ad_sysvol_path} -LogPath ${var.ad_log_path} -NoRebootOnCompletion:$false -Force:$true "
  dc2_install_ad_3 = "-SafeModeAdministratorPassword (ConvertTo-SecureString ${var.ad_safe_mode_administrator_password} -AsPlainText -Force) -CriticalReplicationOnly"

  dc2_shutdown_command   = "shutdown -r -t 10"
  dc2_exit_code_hack     = "exit 0"
  dc2_powershell_command = "${local.dc2_prereq_ad_1}; ${local.dc2_prereq_ad_2}; ${local.dc2_prereq_ad_3}; ${local.dc2_prereq_ad_4}; ${local.dc2_prereq_ad_5}; ${local.dc2_credentials_1}; ${local.dc2_credentials_2}; ${local.dc2_credentials_3}; ${local.dc2_install_ad_1}${local.dc2_install_ad_2}${local.dc2_install_ad_3}; ${local.dc2_shutdown_command}; ${local.dc2_exit_code_hack}"
}

After that, we will use a virtual machine extension to install and configure AD. The “CustomScriptExtension” will execute the inline PowerShell defined above.

resource "azurerm_virtual_machine_extension" "dc2-vm-extension" {
  depends_on=[azurerm_windows_virtual_machine.dc2-vm]

  name                 = "${var.ad_dc2_name}-vm-active-directory"
  virtual_machine_id   = azurerm_windows_virtual_machine.dc2-vm.id
  publisher            = "Microsoft.Compute"
  type                 = "CustomScriptExtension"
  type_handler_version = "1.9"  
  settings = <<SETTINGS
  {
    "commandToExecute": "powershell.exe -Command \"${local.dc2_powershell_command}\""
  }
  SETTINGS
}

18. Creating an NSG (Network Security Group) for Client Machines to AD Domain Controllers

We must open many ports to communicate between client machines and AD Domain Controllers, particularly if our machines are located in different subnets or VNETs.

We will start creating the NSG for the client-to-DC communication:

## Active Directory NSG for Clients ##

# Create the security group for AD Users
resource "azurerm_network_security_group" "active-directory-client-nsg" {
  name                = "active-directory-client-nsg"
  location            = azurerm_resource_group.network-rg.location
  resource_group_name = azurerm_resource_group.network-rg.name
}

And then, we will add the Inbound rules required. In the code above, we will create a security rule for EACH domain controller:

## Inbound Rules ##

# Port 53 DNS UDP
resource "azurerm_network_security_rule" "udp_53_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 53 DNS UDP - DC${count.index+1} Inbound"
  description                 = "AD 53 DNS UDP - DC${count.index+1} Inbound"
  priority                    = (150 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 88 Kerberos TCP
resource "azurerm_network_security_rule" "tcp_88_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 88 Kerberos TCP - DC${count.index+1} Inbound"
  description                 = "AD 88 Kerberos TCP - DC${count.index+1} Inbound"
  priority                    = (160 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "88"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 135 RPC TCP
resource "azurerm_network_security_rule" "tcp_135_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 135 RPC TCP - DC${count.index+1} Inbound"
  description                 = "AD 135 RPC TCP - DC${count.index+1} Inbound"
  priority                    = (170 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "135"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 389 LDAP TCP
resource "azurerm_network_security_rule" "tcp_389_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 389 LDAP TCP - DC${count.index+1} Inbound"
  description                 = "AD 389 LDAP TCP - DC${count.index+1} Inbound"
  priority                    = (180 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "389"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 445 SMB TCP
resource "azurerm_network_security_rule" "tcp_445_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 445 SMB TCP - DC${count.index+1} Inbound"
  description                 = "AD 445 SMB TCP - DC${count.index+1} Inbound"
  priority                    = (190 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "445"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 49152-65535 TCP
resource "azurerm_network_security_rule" "tcp_49152-65535_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 49152-65535 TCP - DC${count.index+1} Inbound"
  description                 = "AD 49152-65535 TCP - DC${count.index+1} Inbound"
  priority                    = (200 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "49152-65535"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 49152-65535 UDP
resource "azurerm_network_security_rule" "udp_49152-65535_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 49152-65535 UDP - DC${count.index+1} Inbound"
  description                 = "AD 49152-65535 UDP - DC${count.index+1} Inbound"
  priority                    = (210 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "49152-65535"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Allow ping AD Domain Controllers
resource "azurerm_network_security_rule" "icmp_client_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD Ping to DC${count.index+1} Inbound"
  description                 = "AD Ping to DC${count.index+1} Inbound"
  priority                    = (220 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Icmp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

After that, we will add the Outbound rules required for EACH domain controller:

## Outbound Rules ##

# Port 53 DNS UDP
resource "azurerm_network_security_rule" "udp_53_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 53 DNS UDP - DC${count.index+1} Outbound"
  description                 = "AD 53 DNS UDP - DC${count.index+1} Outbound"
  priority                    = (150 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 88 Kerberos TCP
resource "azurerm_network_security_rule" "tcp_88_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 88 Kerberos TCP - DC${count.index+1} Outbound"
  description                 = "AD 88 Kerberos TCP - DC${count.index+1} Outbound"
  priority                    = (160 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "88"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 135 RPC TCP
resource "azurerm_network_security_rule" "tcp_135_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 135 RPC TCP - DC${count.index+1} Outbound"
  description                 = "AD 135 RPC TCP - DC${count.index+1} Outbound"
  priority                    = (170 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "135"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 389 LDAP TCP
resource "azurerm_network_security_rule" "tcp_389_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 389 LDAP TCP - DC${count.index+1} Outbound"
  description                 = "AD 389 LDAP TCP - DC${count.index+1} Outbound"
  priority                    = (180 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "389"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 445 SMB TCP
resource "azurerm_network_security_rule" "tcp_445_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 445 SMB TCP - DC${count.index+1} Outbound"
  description                 = "AD 445 SMB TCP - DC${count.index+1} Outbound"
  priority                    = (190 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "445"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 49152-65535 TCP
resource "azurerm_network_security_rule" "tcp_49152-65535_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 49152-65535 TCP - DC${count.index+1} Outbound"
  description                 = "AD 49152-65535 TCP - DC${count.index+1} Outbound"
  priority                    = (200 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "49152-65535"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 49152-65535 UDP
resource "azurerm_network_security_rule" "udp_49152-65535_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 49152-65535 UDP - DC${count.index+1} Outbound"
  description                 = "AD 49152-65535 UDP - DC${count.index+1} Outbound"
  priority                    = (210 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "49152-65535"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Allow ping AD Domain Controllers
resource "azurerm_network_security_rule" "icmp_client_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-client-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD Ping to DC${count.index+1} Outbound"
  description                 = "AD Ping to DC${count.index+1} Outbound"
  priority                    = (220 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Icmp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

19. Creating an NSG (Network Security Group) for Communication Between AD Domain Controllers

We must open many ports to communicate between AD Domain Controllers, particularly if our machines are located in different subnets, VNETs, or on-prem. However, if both AD domain controllers are located on the same subnet, this NSG is not required.

We will start creating the NSG for the client-to-DC communication:

# Create the security group for AD Domain Controllers
resource "azurerm_network_security_group" "active-directory-dc-nsg" {
  name                = "active-directory-dc-nsg"
  location            = azurerm_resource_group.network-rg.location
  resource_group_name = azurerm_resource_group.network-rg.name
}

And then, we will add the Inbound rules required. In the code above, we will create a security rule for EACH domain controller:

## Inbound Rules ##

# Port 53 DNS TCP
resource "azurerm_network_security_rule" "tcp_53_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 53 DNS TCP - DC${count.index+1} Inbound"
  description                 = "AD 53 DNS TCP - DC${count.index+1} Inbound"
  priority                    = (100 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 53 DNS UDP
resource "azurerm_network_security_rule" "udp_53_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 53 DNS UDP - DC${count.index+1} Inbound"
  description                 = "AD 53 DNS UDP - DC${count.index+1} Inbound"
  priority                    = (110 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 88 Kerberos TCP
resource "azurerm_network_security_rule" "tcp_88_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 88 Kerberos TCP - DC${count.index+1} Inbound"
  description                 = "AD 88 Kerberos TCP - DC${count.index+1} Inbound"
  priority                    = (120 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "88"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 88 Kerberos UDP
resource "azurerm_network_security_rule" "udp_88_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 88 Kerberos UDP - DC${count.index+1} Inbound"
  description                 = "AD 88 Kerberos UDP - DC${count.index+1} Inbound"
  priority                    = (130 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "88"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 123 W32Time UDP
resource "azurerm_network_security_rule" "udp_123_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 123 W32Time UDP - DC${count.index+1} Inbound"
  description                 = "AD 123 W32Time UDP - DC${count.index+1} Inbound"
  priority                    = (140 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "123"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 135 RPC TCP
resource "azurerm_network_security_rule" "tcp_135_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 135 RPC TCP - DC${count.index+1} Inbound"
  description                 = "AD 135 RPC TCP - DC${count.index+1} Inbound"
  priority                    = (150 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "135"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 137-138 NetLogon UDP
resource "azurerm_network_security_rule" "udp_137-138_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 137-138 NetLogon UDP - DC${count.index+1} Inbound"
  description                 = "AD 137-138 NetLogon UDP - DC${count.index+1} Inbound"
  priority                    = (160 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "137-138"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 139 NetLogon TCP
resource "azurerm_network_security_rule" "tcp_139_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 139 NetLogon TCP - DC${count.index+1} Inbound"
  description                 = "AD 139 NetLogon TCP - DC${count.index+1} Inbound"
  priority                    = (170 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "139"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 389 LDAP TCP
resource "azurerm_network_security_rule" "tcp_389_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 389 LDAP TCP - DC${count.index+1} Inbound"
  description                 = "AD 389 LDAP TCP - DC${count.index+1} Inbound"
  priority                    = (180 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "389"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 389 LDAP UDP
resource "azurerm_network_security_rule" "udp_389_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 389 LDAP UDP - DC${count.index+1} Inbound"
  description                 = "AD 389 LDAP UDP - DC${count.index+1} Inbound"
  priority                    = (190 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "389"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 445 SMB TCP
resource "azurerm_network_security_rule" "tcp_445_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 445 SMB TCP - DC${count.index+1} Inbound"
  description                 = "AD 445 SMB TCP - DC${count.index+1} Inbound"
  priority                    = (200 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "445"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 464 Kerberos Authentication TCP
resource "azurerm_network_security_rule" "tcp_464_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 464 Kerberos Authentication TCP - DC${count.index+1} Inbound"
  description                 = "AD 464 Kerberos Authentication TCP - DC${count.index+1} Inbound"
  priority                    = (210 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "464"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 464 Kerberos Authentication UDP
resource "azurerm_network_security_rule" "udp_464_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 464 Kerberos Authentication UDP - DC${count.index+1} Inbound"
  description                 = "AD 464 Kerberos Authentication UDP - DC${count.index+1} Inbound"
  priority                    = (220 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "464"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 636 LDAP SSL TCP
resource "azurerm_network_security_rule" "tcp_636_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 636 LDAP SSL TCP - DC${count.index+1} Inbound"
  description                 = "AD 636 LDAP SSL TCP - DC${count.index+1} Inbound"
  priority                    = (230 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "636"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 3268-3269 LDAP GC TCP
resource "azurerm_network_security_rule" "tcp_3268-3269_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 3268-3269 LDAP GC TCP - DC${count.index+1} Inbound"
  description                 = "AD 3268-3269 LDAP GC TCP - DC${count.index+1} Inbound"
  priority                    = (240 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "3268-3269"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 49152-65535 TCP
resource "azurerm_network_security_rule" "tcp_49152-65535_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 49152-65535 TCP - DC${count.index+1} Inbound"
  description                 = "AD 49152-65535 TCP - DC${count.index+1} Inbound"
  priority                    = (250 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "49152-65535"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Port 49152-65535 UDP
resource "azurerm_network_security_rule" "udp_49152-65535_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 49152-65535 UDP - DC${count.index+1} Inbound"
  description                 = "AD 49152-65535 UDP - DC${count.index+1} Inbound"
  priority                    = (260 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "49152-65535"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

# Allow ping AD Domain Controllers
resource "azurerm_network_security_rule" "icmp_dc_inbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD Ping to DC${count.index+1} Inbound"
  description                 = "AD Ping to DC${count.index+1} Inbound"
  priority                    = (270 + count.index)
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Icmp"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = local.dns_servers[count.index]
  destination_address_prefix  = "*"
}

After that, we will add the Outbound rules required for EACH domain controller:

## Outbound Rules ##

# Port 53 DNS TCP
resource "azurerm_network_security_rule" "tcp_53_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 53 DNS TCP - DC${count.index+1} Outbound"
  description                 = "AD 53 DNS TCP - DC${count.index+1} Outbound"
  priority                    = (100 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 53 DNS UDP
resource "azurerm_network_security_rule" "udp_53_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 53 DNS UDP - DC${count.index+1} Outbound"
  description                 = "AD 53 DNS UDP - DC${count.index+1} Outbound"
  priority                    = (110 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "53"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 88 Kerberos TCP
resource "azurerm_network_security_rule" "tcp_88_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 88 Kerberos TCP - DC${count.index+1} Outbound"
  description                 = "AD 88 Kerberos TCP - DC${count.index+1} Outbound"
  priority                    = (120 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "88"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 88 Kerberos UDP
resource "azurerm_network_security_rule" "udp_88_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 88 Kerberos UDP - DC${count.index+1} Outbound"
  description                 = "AD 88 Kerberos UDP - DC${count.index+1} Outbound"
  priority                    = (130 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "88"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 123 W32Time UDP
resource "azurerm_network_security_rule" "udp_123_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 123 W32Time UDP - DC${count.index+1} Outbound"
  description                 = "AD 123 W32Time UDP - DC${count.index+1} Outbound"
  priority                    = (140 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "123"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 135 RPC TCP
resource "azurerm_network_security_rule" "tcp_135_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 135 RPC TCP - DC${count.index+1} Outbound"
  description                 = "AD 135 RPC TCP - DC${count.index+1} Outbound"
  priority                    = (150 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "135"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 137-138 NetLogon UDP
resource "azurerm_network_security_rule" "udp_137-138_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 137-138 NetLogon UDP - DC${count.index+1} Outbound"
  description                 = "AD 137-138 NetLogon UDP - DC${count.index+1} Outbound"
  priority                    = (160 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "137-138"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 139 NetLogon TCP
resource "azurerm_network_security_rule" "tcp_139_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 139 NetLogon TCP - DC${count.index+1} Outbound"
  description                 = "AD 139 NetLogon TCP - DC${count.index+1} Outbound"
  priority                    = (170 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "139"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 389 LDAP TCP
resource "azurerm_network_security_rule" "tcp_389_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 389 LDAP TCP - DC${count.index+1} Outbound"
  description                 = "AD 389 LDAP TCP - DC${count.index+1} Outbound"
  priority                    = (180 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "389"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 389 LDAP UDP
resource "azurerm_network_security_rule" "udp_389_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 389 LDAP UDP - DC${count.index+1} Outbound"
  description                 = "AD 389 LDAP UDP - DC${count.index+1} Outbound"
  priority                    = (190 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "389"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 445 SMB TCP
resource "azurerm_network_security_rule" "tcp_445_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 445 SMB TCP - DC${count.index+1} Outbound"
  description                 = "AD 445 SMB TCP - DC${count.index+1} Outbound"
  priority                    = (200 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "445"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 464 Kerberos Authentication TCP
resource "azurerm_network_security_rule" "tcp_464_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 464 Kerberos Authentication TCP - DC${count.index+1} Outbound"
  description                 = "AD 464 Kerberos Authentication TCP - DC${count.index+1} Outbound"
  priority                    = (210 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "464"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 464 Kerberos Authentication UDP
resource "azurerm_network_security_rule" "udp_464_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 464 Kerberos Authentication UDP - DC${count.index+1} Outbound"
  description                 = "AD 464 Kerberos Authentication UDP - DC${count.index+1} Outbound"
  priority                    = (220 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Udp"
  source_port_range           = "*"
  destination_port_range      = "464"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
}

# Port 636 LDAP SSL TCP
resource "azurerm_network_security_rule" "tcp_636_dc_outbound" {
  depends_on = [azurerm_resource_group.network-rg]

  count = length(local.dns_servers)

  network_security_group_name = azurerm_network_security_group.active-directory-dc-nsg.name
  resource_group_name         = azurerm_resource_group.network-rg.name
  name                        = "AD 636 LDAP SSL TCP - DC${count.index+1} Outbound"
  description                 = "AD 636 LDAP SSL TCP - DC${count.index+1} Outbound"
  priority                    = (230 + count.index)
  direction                   = "Outbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "636"
  source_address_prefix       = "*"
  destination_address_prefix  = local.dns_servers[count.index]
8. Creating the Input Definition Variables File
In the last step, we will create the input definition variables file “terraform.tfvars” and add the following code. The input variables let us customize aspects of Terraform modules without altering the source code.

# Common Variables #
location = "northeurope"

# Authentication #
azure-tenant-id       = "complete-this"
azure-subscription-id = "complete-this"
azure-client-id       = "complete-this"
azure-client-secret   = "complete-this"

# Network #
network-vnet-cidr           = "10.128.0.0/16"
network-vm-subnet-cidr      = "10.128.1.0/24"
network-bastion-subnet-cidr = "10.128.2.0/24"}

20. Creating the Input Definition Variables File

In the last step, we will create the input definition variables file “terraform.tfvars” and add the following code. The input variables let us customize aspects of Terraform modules without altering the source code.

# Common Variables #
location = "northeurope"

# Authentication #
azure-tenant-id       = "complete-this"
azure-subscription-id = "complete-this"
azure-client-id       = "complete-this"
azure-client-secret   = "complete-this"

# Network #
network-vnet-cidr      = "10.129.0.0/16"
network-vm-subnet-cidr = "10.129.1.0/24"

# Active Directory #
ad_domain_name                      = "kopicloud.local"
ad_domain_netbios_name              = "kopicloud"
ad_admin_username                   = "kopiadmin"
ad_admin_password                   = "L30M3ss110"
ad_safe_mode_administrator_password = "R3c0v3ryAcc3ssM0d3"

# Domain Controllers #
ad_dc1_name       = "kopi-dev-dc1"
ad_dc1_ip_address = "10.129.1.11"
dc1_vm_size       = "Standard_B2s"

ad_dc2_name       = "kopi-dev-dc2"
ad_dc2_ip_address = "10.129.1.12"
dc2_vm_size       = "Standard_B2s"

21. Deployment Notes:

To deploy the code:

  1. Clone the repo to your computer
  2. Move the files “vm-dc2-main.tf” and “vm-dc2-output.tf” outside the folder
  3. Execute “terraform init”
  4. Execute “terraform apply”
  5. When execution is complete and the DC1 is running, copy the files “vm-dc2-main.tf “, and “vm-dc2-output.tf” back to the folder
  6. Execute “terraform apply” to create the DC2

Note: if you execute both DC1 and DC2 together, DC2 will fail to join the domain, as the setup of the AD domain takes several minutes.

The complete code to deploy the Active Directory (AD) Domain Controller (DC) Virtual Machine (VM) in Azure with Terraform is here → https://github.com/KopiCloud/terraform-azure-active-directory-dc-vm

And that’s all, folks. If you liked this story, please show your support by 👏 for this story. Thank you for reading!

Azure
Terraform
Active Directory
Domain Controller
Virtual Machine
Recommended from ReadMedium