-
Notifications
You must be signed in to change notification settings - Fork 2
/
Create-and-Attach-AzureBastion-private-only-NSG.ps1
318 lines (227 loc) · 18.9 KB
/
Create-and-Attach-AzureBastion-private-only-NSG.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
<#
.SYNOPSIS
A script used to create and attach an NSG to the AzureBastionSubnet for an Azure Bastion private-only deployment (Public Preview) in a management subscription.
.DESCRIPTION
A script used to create and attach an NSG to the AzureBastionSubnet for an Azure Bastion private-only deployment (Public Preview) in a management subscription.
The script will do all of the following:
Remove the breaking change warning messages.
Change the current context to use a management subscription (a subscription with *management* in the subscription name will be automatically selected).
Save the Log Analytics workspace from the management subscription in a variable.
Store a specified set of tags in a hash table.
If it does not already exist, create a resource group for the storage account that will store the NSG flow log data.
If it does not already exist, create a general-purpose v2 storage account for storing the flow logs with specific configuration settings. Also apply the necessary tags to this storage account.
Create the AzureBastionSubnet with the network security group if it does not already exist. Add the required inbound and outbound security rules. Add specified tags and diagnostic settings.
Enable NSG Flow logs (Version 2) and Traffic Analytics for the AzureBastionSubnet NSG.
.NOTES
Filename: Create-and-Attach-AzureBastion-private-only-NSG.ps1
Created: 10/06/2024
Last modified: 10/06/2024
Author: Wim Matthyssen
Version: 1.0
PowerShell: Azure PowerShell and Azure Cloud Shell
Requires: PowerShell Az (v10.4.1) and Az.Network (v6.2.0)
Action: Change variables were needed to fit your needs.
Disclaimer: This script is provided "as is" with no warranties.
.EXAMPLE
Connect-AzAccount
Get-AzTenant (if not using the default tenant)
Set-AzContext -tenantID "xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx" (if not using the default tenant)
.\Create-and-Attach-AzureBastion-private-only-NSG.ps1
.LINK
https://wmatthyssen.com/2024/06/11/azure-bastion-solving-vm-connection-problems-caused-by-nsg-on-azurebastionsubnet-when-using-the-azure-bastion-premium-sku/
#>
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Variables
$spoke = "hub"
$region = #<your region here> The used Azure public region. Example: "westeurope"
$purpose = "Bastion"
$rgNameNetworking = #<your VNet resource group name here> The name of the Azure resource group in which you're existing VNet is deployed. Example: "rg-hub-myh-networking-01"
$rgNameStorage = #<your storage account resource group name here> The name of the Azure resource group in which you're new or existing storage account is deployed. Example: "rg-hub-myh-storage-01"
$rgNameNetworkWatcher = #<your Network Watcher resource group name here> The name of the Azure resource group in which you're existing Network Watcher is deployed. Example: "rg-hub-myh-networking-01"
$networkWatcherName = #<your Network Watcher name here> The name of your existing Network Watcher. Example: "nw-hub-myh-we-01"
$logAnalyticsWorkspaceName = #<your Log Analytics workspace name here> The name of your existing Log Analytics workspace. Example: "law-hub-myh-01"
$storageAccountName = #<your storage account name here> The existing or new storage account to store the NSG Flow logs. Example: "sthubmyhlog01"
$storageAccountSkuName = "Standard_LRS"
$storageAccountType = "StorageV2"
$storageMinimumTlsVersion = "TLS1_2"
$nsgFlowLogsRetention = "90"
$trafficAnalyticsInterval = "60"
$vnetName = #<your VNet name here> The existing VNet in which the Bastion resource will be created. Example: "vnet-hub-myh-weu-01"
$subnetNameBastion = "AzureBastionSubnet"
$subnetAddressBastion = #<your AzureBastionSubnet range here> The subnet must have a minimum subnet size of /26. Example: "10.1.1.128/26"
$nsgNameBastion = #<your AzureBastionSubnet NSG name here> The name of the NSG associated with the AzureBastionSubnet. Example: "nsg-AzureBastionSubnet"
$nsgBastionDiagnosticsName = #<your NSG Bastion Diagnostics settings name here> The name of the NSG diagnostic settings for Bastion. Example: "diag-nsg-AzureBastionSubnet"
$tagSpokeName = #<your environment tag name here> The environment tag name you want to use. Example:"Env"
$tagSpokeValue = "$($spoke[0].ToString().ToUpper())$($spoke.SubString(1))"
$tagCostCenterName = #<your costCenter tag name here> The costCenter tag name you want to use. Example:"CostCenter"
$tagCostCenterValue = #<your costCenter tag value here> The costCenter tag value you want to use. Example: "23"
$tagCriticalityName = #<your businessCriticality tag name here> The businessCriticality tag name you want to use. Example: "Criticality"
$tagCriticalityValue = #<your businessCriticality tag value here> The businessCriticality tag value you want to use. Example: "High"
$tagPurposeName = #<your purpose tag name here> The purpose tag name you want to use. Example:"Purpose"
$tagPurposeValueBastion = "$($purpose[0].ToString().ToUpper())$($purpose.SubString(1))"
$tagPurposeValueStorage = "Storage"
$tagPurposeValueLog = "Log"
$tagSkuName = "Sku"
$tagSkuValue = $storageAccountSkuName
$global:currenttime= Set-PSBreakpoint -Variable currenttime -Mode Read -Action {$global:currenttime= Get-Date -UFormat "%A %m/%d/%Y %R"}
$foregroundColor1 = "Green"
$foregroundColor2 = "Yellow"
$writeEmptyLine = "`n"
$writeSeperatorSpaces = " - "
# ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Remove the breaking change warning messages
Set-Item -Path Env:\SuppressAzurePowerShellBreakingChangeWarnings -Value $true | Out-Null
Update-AzConfig -DisplayBreakingChangeWarning $false | Out-Null
$warningPreference = "SilentlyContinue"
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script started
Write-Host ($writeEmptyLine + "# Script started. Without errors, it can take up to 2 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Change the current context to use a management subscription
$subNameManagement = Get-AzSubscription | Where-Object {$_.Name -like "*management*"}
Set-AzContext -SubscriptionId $subNameManagement.SubscriptionId | Out-Null
Write-Host ($writeEmptyLine + "# Management subscription in current tenant selected" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Save Log Analytics workspace from the management subscription in a variable
$workSpace = Get-AzOperationalInsightsWorkspace | Where-Object Name -Match $logAnalyticsWorkSpaceName
Write-Host ($writeEmptyLine + "# Log Analytics workspace variable created" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Store the specified set of tags in a hash table
$tags = @{$tagSpokeName=$tagSpokeValue;$tagCostCenterName=$tagCostCenterValue;$tagCriticalityName=$tagCriticalityValue}
Write-Host ($writeEmptyLine + "# Specified set of tags available to add" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## If it does not already exist, create a resource group for the storage account that will store the NSG flow log data.
try {
Get-AzResourceGroup -Name $rgNameStorage -ErrorAction Stop | Out-Null
} catch {
New-AzResourceGroup -Name $rgNameStorage -Location $region -Force | Out-Null
}
# Save variable tags in a new variable to add tags
$tagsResourceGroup = $tags
# Add Purpose tag to tagsResourceGroup
$tagsResourceGroup += @{$tagPurposeName = $tagPurposeValueStorage}
# Set tags rg storage
Set-AzResourceGroup -Name $rgNameStorage -Tag $tagsResourceGroup | Out-Null
Write-Host ($writeEmptyLine + "# Resource group $rgNameStorage available" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## If it does not already exist, create a general-purpose v2 storage account for storing the flow logs with specific configuration settings.
## Also apply the necessary tags to this storage account.
try {
Get-AzStorageAccount -ResourceGroupName $rgNameStorage -Name $storageAccountName -ErrorAction Stop | Out-Null
} catch {
New-AzStorageAccount -ResourceGroupName $rgNameStorage -Name $storageAccountName -SkuName $storageAccountSkuName -Location $region -Kind $storageAccountType `
-AllowBlobPublicAccess $false -MinimumTlsVersion $storageMinimumTlsVersion | Out-Null
}
# Save variable tags in a new variable to add tags
$tagsStorageAccount = $tags
# Add Purpose tag to tagsStorageAccount
$tagsStorageAccount += @{$tagPurposeName = $tagPurposeValueLog}
# Add Sku tag to tagsStorageAccount
$tagsStorageAccount += @{$tagSkuName = $tagSkuValue}
# Set tags storage account
Set-AzStorageAccount -ResourceGroupName $rgNameStorage -Name $storageAccountName -Tag $tagsStorageAccount | Out-Null
Write-Host ($writeEmptyLine + "# Storage account $storageAccountName created" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Create the AzureBastionSubnet with the network security group if it does not already exist. Add the required inbound and outbound security rules. Add specified tags and diagnostic settings
# Inbound rules
# Rule to allow Ingress Traffic from public Internet
$inboundRule1 = New-AzNetworkSecurityRuleConfig -Name "Allow_TCP_443_Internet_Inbound" -Description "Allow_TCP_443_Internet_Inbound" `
-Access Allow -Protocol TCP -Direction Inbound -Priority 100 -SourceAddressPrefix Internet -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange 443
# Rule to allow Ingress Traffic to Azure Bastion data plane
$inboundRule2 = New-AzNetworkSecurityRuleConfig -Name "Allow_Any_8080_5701_BastionHostCommunication_Inbound" -Description "Allow_Any_8080_5701_BastionHostCommunication_Inbound" `
-Access Allow -Protocol * -Direction Inbound -Priority 110 -SourceAddressPrefix VirtualNetwork -SourcePortRange * -DestinationAddressPrefix VirtualNetwork `
-DestinationPortRange 8080,5701
# Rule to allow Ingress Traffic to Virtual Network
$inboundRule3 = New-AzNetworkSecurityRuleConfig -Name "Allow_Any_Any_Vnet_Inbound" -Description "Allow_Any_Any_Vnet_Inbound" `
-Access Allow -Protocol * -Direction Inbound -Priority 120 -SourceAddressPrefix VirtualNetwork -SourcePortRange * -DestinationAddressPrefix VirtualNetwork `
-DestinationPortRange *
# Rule to deny all other inbound virtual network traffic
$inboundRule4 = New-AzNetworkSecurityRuleConfig -Name "Deny_Any_Other_Traffic_Inbound" -Description "Deny_Any_Other_Inbound_Traffic_Inbound" `
-Access Deny -Protocol * -Direction Inbound -Priority 900 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange *
# Outbound rules
# Rule to allow Egress Traffic to target VMs via RDP (TCP and UDP)
$outboundRule1 = New-AzNetworkSecurityRuleConfig -Name "Allow_Any_3389_VirtualNetwork_Outbound" -Description "Allow_Any_3389_VirtualNetwork_Outbound" `
-Access Allow -Protocol * -Direction Outbound -Priority 100 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix VirtualNetwork -DestinationPortRange 3389
# Rule to allow Egress Traffic to target VMs via SSH (TCP and UPD)
$outboundRule2 = New-AzNetworkSecurityRuleConfig -Name "Allow_Any_22_VirtualNetwork_Outbound" -Description "Allow_Any_22_VirtualNetwork_Outbound" `
-Access Allow -Protocol * -Direction Outbound -Priority 110 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix VirtualNetwork -DestinationPortRange 22
# Rule to allow Egress Traffic to other public endpoints in Azure (e.g., for storing diagnostics logs and metering logs)
$outboundRule3 = New-AzNetworkSecurityRuleConfig -Name "Allow_TCP_443_AzureCloud_Outbound" -Description "Allow_TCP_443_AzureCloud_Outbound" `
-Access Allow -Protocol TCP -Direction Outbound -Priority 120 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix AzureCloud -DestinationPortRange 443
# Rule to allow Egress Traffic to Azure Bastion data plane
$outboundRule4 = New-AzNetworkSecurityRuleConfig -Name "Allow_Any_8080_5701_BastionHostCommunication_Outbound" -Description "Allow_Any_8080_5701_BastionHostCommunication_Outbound" `
-Access Allow -Protocol * -Direction Outbound -Priority 130 -SourceAddressPrefix VirtualNetwork -SourcePortRange * -DestinationAddressPrefix VirtualNetwork `
-DestinationPortRange 8080,5701
# Rule to allow Egress Traffic to Internet to allow Azure Bastion to communicate with the Internet for session and certificate validation
$outboundRule5 = New-AzNetworkSecurityRuleConfig -Name "Allow_Any_80_Internet_Outbound" -Description "Allow_Any_80_Internet_Outbound" `
-Access Allow -Protocol * -Direction Outbound -Priority 140 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix Internet `
-DestinationPortRange 80
# Rule to deny all other outbound virtual network traffic
$outboundRule6 = New-AzNetworkSecurityRuleConfig -Name "Deny_Any_Other_Traffic_Outbound" -Description "Deny_Any_Other_Outbound_Traffic_Outbound" `
-Access Deny -Protocol * -Direction Outbound -Priority 900 -SourceAddressPrefix * -SourcePortRange * -DestinationAddressPrefix * -DestinationPortRange *
# Create the NSG if it does not exist
try {
Get-AzNetworkSecurityGroup -Name $nsgNameBastion -ResourceGroupName $rgNameNetworking -ErrorAction Stop | Out-Null
} catch {
New-AzNetworkSecurityGroup -Name $nsgNameBastion -ResourceGroupName $rgNameNetworking -Location $region `
-SecurityRules $inboundRule1,$inboundRule2,$inboundRule3,$inboundRule4,$outboundRule1,$outboundRule2,$outboundRule3,$outboundRule4,$outboundRule5,`
$outboundRule6 -Force | Out-Null
}
# Save variable tags in a new variable to add tags
$tagsBastion = $tags
# Add Purpose tag to $tagsBastion
$tagsBastion += @{$tagPurposeName = $tagPurposeValueBastion}
# Set tags NSG
$nsg = Get-AzNetworkSecurityGroup -Name $nsgNameBastion -ResourceGroupName $rgNameNetworking
$nsg.Tag = $tagsBastion
Set-AzNetworkSecurityGroup -NetworkSecurityGroup $nsg | Out-Null
Write-Host ($writeEmptyLine + "# NSG $nsgNameBastion available" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
# Set the log settings for the NSG if they don't exist
$nsgLog = @()
$nsgLog += New-AzDiagnosticSettingLogSettingsObject -Enabled $true -CategoryGroup "allLogs"
try {
Get-AzDiagnosticSetting -Name $nsgBastionDiagnosticsName -ResourceId ($nsg.Id) -ErrorAction Stop | Out-Null
} catch {
New-AzDiagnosticSetting -Name $nsgBastionDiagnosticsName -ResourceId ($nsg.Id) -Log $nsgLog `
-WorkspaceId ($workSpace.ResourceId) | Out-Null
}
# Create the AzureBastionSubnet if it does not exist
try {
$vnet = Get-AzVirtualNetwork -Name $vnetName -ResourceGroupname $rgNameNetworking
$subnet = Get-AzVirtualNetworkSubnetConfig -Name $subnetNameBastion -VirtualNetwork $vnet -ErrorAction Stop | Out-Null
} catch {
$subnet = Add-AzVirtualNetworkSubnetConfig -Name $subnetNameBastion -VirtualNetwork $vnet -AddressPrefix $subnetAddressBastion | Out-Null
$vnet | Set-AzVirtualNetwork | Out-Null
}
# Attach the NSG to the AzureBastionSubnet (also if the AzureBastionSubnet exists but lacks an NSG)
$subnet = Get-AzVirtualNetworkSubnetConfig -Name $subnetNameBastion -VirtualNetwork $vnet
$nsg = Get-AzNetworkSecurityGroup -Name $nsgNameBastion -ResourceGroupName $rgNameNetworking
$subnet.NetworkSecurityGroup = $nsg
$vnet | Set-AzVirtualNetwork | Out-Null
Write-Host ($writeEmptyLine + "# Subnet $subnetNameBastion available with attached NSG $nsgNameBastion" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Enable NSG Flow logs (Version 2) and Traffic Analytics for the AzureBastionSubnet NSG
$networkWatcher = Get-AzNetworkWatcher -Name $networkWatcherName -ResourceGroupName $rgNameNetworkWatcher
$storageAccount = Get-AzStorageAccount -ResourceGroupName $rgNameStorage -Name $storageAccountName
try {
Get-AzNetworkWatcherFlowLog -Name "$($nsg.Name)-flow-log" -NetworkWatcher $networkWatcher -TargetResourceId $nsg.Id -ErrorAction Stop | Out-Null
} catch {
# Configure Flow log and Traffic Analytics
Set-AzNetworkWatcherFlowLog -Name "$($nsg.Name)-flow-log" -NetworkWatcher $networkWatcher -TargetResourceId $nsg.Id -StorageId $storageAccount.Id -Enabled $true -FormatType Json `
-FormatVersion 2 -EnableTrafficAnalytics -TrafficAnalyticsWorkspaceId ($workSpace.ResourceId) -TrafficAnalyticsInterval $trafficAnalyticsInterval -EnableRetention $true `
-RetentionPolicyDays $nsgFlowLogsRetention -Tag $tagsBastion -Force | Out-Null
}
Write-Host ($writeEmptyLine + "# NSG FLow logs and Traffic Analytics for $($nsg.Name) enabled" + $writeSeperatorSpaces + $currentTime) -foregroundcolor $foregroundColor2 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
## Write script completed
Write-Host ($writeEmptyLine + "# Script completed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine
## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------