Skip to content

Commit 98bec11

Browse files
authored
Azure Firewall - Data PIP removal for FT firewal (#14138)
* validation pip * help msg * remove data pip validation * add log * logic for add & remote pip * change pip config name * comments & tests * add test recording * update new-azfw help for no data pip feature * update new-azfw doc, -PublicIpName cmd
1 parent f1c2d8f commit 98bec11

File tree

7 files changed

+5401
-44
lines changed

7 files changed

+5401
-44
lines changed

src/Network/Network.Test/ScenarioTests/AzureFirewallTests.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,13 @@ public void TestAzureFirewallCRUDWithAllowActiveFTP()
112112
{
113113
TestRunner.RunTestScript("Test-AzureFirewallCRUDAllowActiveFTP");
114114
}
115+
116+
[Fact]
117+
[Trait(Category.AcceptanceType, Category.CheckIn)]
118+
[Trait(Category.Owner, NrpTeamAlias.azurefirewall)]
119+
public void TestAzureFirewallNoDataPip()
120+
{
121+
TestRunner.RunTestScript("Test-AzureFirewallNoDataPip");
122+
}
115123
}
116124
}

src/Network/Network.Test/ScenarioTests/AzureFirewallTests.ps1

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,4 +1527,90 @@ function Test-AzureFirewallCRUDAllowActiveFTP {
15271527
# Cleanup
15281528
Clean-ResourceGroup $rgname
15291529
}
1530+
}
1531+
1532+
<#
1533+
.SYNOPSIS
1534+
Tests AzureFirewall NoDataPip
1535+
#>
1536+
function Test-AzureFirewallNoDataPip {
1537+
# Setup
1538+
$rgname = Get-ResourceGroupName
1539+
$azureFirewallName = Get-ResourceName
1540+
$resourceTypeParent = "Microsoft.Network/AzureFirewalls"
1541+
$location = Get-ProviderLocation $resourceTypeParent "centraluseuap"
1542+
1543+
$vnetName = Get-ResourceName
1544+
$subnetName = "AzureFirewallSubnet"
1545+
$mgmtSubnetName = "AzureFirewallManagementSubnet"
1546+
$mgmtPublicIpName = Get-ResourceName
1547+
$publicIp1Name = Get-ResourceName
1548+
1549+
try {
1550+
# Create the resource group
1551+
$resourceGroup = New-AzResourceGroup -Name $rgname -Location $location -Tags @{ testtag = "testval" }
1552+
1553+
# Create the Virtual Network
1554+
$subnet = New-AzVirtualNetworkSubnetConfig -Name $subnetName -AddressPrefix 10.0.0.0/24
1555+
$mgmtSubnet = New-AzVirtualNetworkSubnetConfig -Name $mgmtSubnetName -AddressPrefix 10.0.100.0/24
1556+
$vnet = New-AzVirtualNetwork -Name $vnetName -ResourceGroupName $rgname -Location $location -AddressPrefix 10.0.0.0/16 -Subnet $subnet,$mgmtSubnet
1557+
1558+
# Get full subnet details
1559+
$subnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $subnetName
1560+
$mgmtSubnet = Get-AzVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $mgmtSubnetName
1561+
1562+
# Create mgmt public ip
1563+
$mgmtPublicIp = New-AzPublicIpAddress -ResourceGroupName $rgname -name $mgmtPublicIpName -location $location -AllocationMethod Static -Sku Standard
1564+
1565+
# Create AzureFirewall with a management IP, without data PIP
1566+
$azureFirewall = New-AzFirewall -Name $azureFirewallName -ResourceGroupName $rgname -Location $location -VirtualNetwork $vnet -ManagementPublicIpAddress $mgmtPublicIp
1567+
1568+
# Get AzureFirewall
1569+
$getAzureFirewall = Get-AzFirewall -name $azureFirewallName -ResourceGroupName $rgname
1570+
1571+
# Verify creating firewall without data PIP
1572+
Assert-AreEqual $rgName $getAzureFirewall.ResourceGroupName
1573+
Assert-AreEqual $azureFirewallName $getAzureFirewall.Name
1574+
Assert-NotNull $getAzureFirewall.Location
1575+
Assert-AreEqual (Normalize-Location $location) $getAzureFirewall.Location
1576+
Assert-NotNull $getAzureFirewall.Etag
1577+
Assert-AreEqual 1 @($getAzureFirewall.IpConfigurations).Count
1578+
Assert-Null $getAzureFirewall.IpConfigurations[0].PublicIpAddress.Id
1579+
Assert-NotNull $getAzureFirewall.IpConfigurations[0].Subnet.Id
1580+
Assert-NotNull $getAzureFirewall.IpConfigurations[0].PrivateIpAddress
1581+
Assert-AreEqual $subnet.Id $getAzureFirewall.IpConfigurations[0].Subnet.Id
1582+
Assert-NotNull $getAzureFirewall.ManagementIpConfiguration
1583+
Assert-NotNull $getAzureFirewall.ManagementIpConfiguration.Subnet.Id
1584+
Assert-NotNull $getAzureFirewall.ManagementIpConfiguration.PublicIpAddress.Id
1585+
Assert-AreEqual $mgmtSubnet.Id $getAzureFirewall.ManagementIpConfiguration.Subnet.Id
1586+
Assert-AreEqual $mgmtPublicIp.Id $getAzureFirewall.ManagementIpConfiguration.PublicIpAddress.Id
1587+
1588+
# Verify adding a data PIP to firewall
1589+
$publicip1 = New-AzPublicIpAddress -ResourceGroupName $rgname -name $publicIp1Name -location $location -AllocationMethod Static -Sku Standard
1590+
$getAzureFirewall.AddPublicIpAddress($publicip1)
1591+
Set-AzFirewall -AzureFirewall $getAzureFirewall
1592+
Assert-NotNull $getAzureFirewall.IpConfigurations[0].PublicIpAddress.Id
1593+
Assert-AreEqual $publicip1.Id $getAzureFirewall.IpConfigurations.PublicIpAddress.Id
1594+
1595+
# Verify removing data PIP from exisiting FT firewall
1596+
$getAzureFirewall.RemovePublicIpAddress($publicip1)
1597+
Set-AzFirewall -AzureFirewall $getAzureFirewall
1598+
Assert-AreEqual 1 @($getAzureFirewall.IpConfigurations).Count
1599+
Assert-Null $getAzureFirewall.IpConfigurations[0].PublicIpAddress.Id
1600+
1601+
# Delete AzureFirewall
1602+
$delete = Remove-AzFirewall -ResourceGroupName $rgname -name $azureFirewallName -PassThru -Force
1603+
Assert-AreEqual true $delete
1604+
1605+
# Delete VirtualNetwork
1606+
$delete = Remove-AzVirtualNetwork -ResourceGroupName $rgname -name $vnetName -PassThru -Force
1607+
Assert-AreEqual true $delete
1608+
1609+
$list = Get-AzFirewall -ResourceGroupName $rgname
1610+
Assert-AreEqual 0 @($list).Count
1611+
}
1612+
finally {
1613+
# Cleanup
1614+
Clean-ResourceGroup $rgname
1615+
}
15301616
}

src/Network/Network.Test/SessionRecords/Commands.Network.Test.ScenarioTests.AzureFirewallTests/TestAzureFirewallNoDataPip.json

Lines changed: 5220 additions & 0 deletions
Large diffs are not rendered by default.

src/Network/Network/AzureFirewall/NewAzureFirewallCommand.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class NewAzureFirewallCommand : AzureFirewallBaseCmdlet
8383
NewParameterTypeName = "List<PSPublicIpAddress>",
8484
ReplaceMentCmdletParameterName = "PublicIpAddress")]
8585
[Parameter(
86-
Mandatory = true,
86+
Mandatory = false,
8787
ValueFromPipelineByPropertyName = true,
8888
ParameterSetName = "OldIpConfigurationParameterValues",
8989
HelpMessage = "Public IP address name. The Public IP must use Standard SKU and must belong to the same resource group as the Firewall.")]
@@ -99,7 +99,7 @@ public class NewAzureFirewallCommand : AzureFirewallBaseCmdlet
9999
public PSVirtualNetwork VirtualNetwork { get; set; }
100100

101101
[Parameter(
102-
Mandatory = true,
102+
Mandatory = false,
103103
ValueFromPipelineByPropertyName = true,
104104
ParameterSetName = "IpConfigurationParameterValues",
105105
HelpMessage = "One or more Public IP Addresses. The Public IP addresses must use Standard SKU and must belong to the same resource group as the Firewall.")]

src/Network/Network/ChangeLog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
- Added deprecation attribute warning to the old cmdlets.
5353
* Bug fix in ExpressRouteLink MacSecConfig. Added new property `SciState` to `PSExpressRouteLinkMacSecConfig`
5454
* Updated format list and format table views for Get-AzVirtualNetworkGatewayConnectionIkeSa
55+
* Updated New-AzFirewall to no longer require data public IP for force tunneling firewall (with management IP and subnet)
5556

5657
## Version 4.5.0
5758
* Added new cmdlets for CRUD of VpnGatewayNATRule.

src/Network/Network/Models/AzureFirewall/PSAzureFirewall.cs

Lines changed: 66 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,12 @@ public void Allocate(PSVirtualNetwork virtualNetwork, PSPublicIpAddress[] public
137137
throw new ArgumentNullException(nameof(virtualNetwork), "Virtual Network cannot be null!");
138138
}
139139

140-
if (publicIpAddresses == null || publicIpAddresses.Count() == 0)
140+
if (ManagementPublicIpAddress == null)
141141
{
142-
throw new ArgumentNullException(nameof(publicIpAddresses), "Public IP Addresses cannot be null or empty!");
142+
if (publicIpAddresses == null || publicIpAddresses.Count() == 0)
143+
{
144+
throw new ArgumentNullException(nameof(publicIpAddresses), "Public IP Addresses cannot be null or empty!");
145+
}
143146
}
144147

145148
PSSubnet firewallSubnet = null;
@@ -151,7 +154,7 @@ public void Allocate(PSVirtualNetwork virtualNetwork, PSPublicIpAddress[] public
151154
{
152155
throw new ArgumentException($"Virtual Network {virtualNetwork.Name} should contain a Subnet named {AzureFirewallSubnetName}");
153156
}
154-
157+
155158
PSSubnet firewallMgmtSubnet = null;
156159
if (ManagementPublicIpAddress != null)
157160
{
@@ -174,14 +177,21 @@ public void Allocate(PSVirtualNetwork virtualNetwork, PSPublicIpAddress[] public
174177

175178
this.IpConfigurations = new List<PSAzureFirewallIpConfiguration>();
176179

177-
for (var i = 0; i < publicIpAddresses.Count(); i++)
180+
if (publicIpAddresses != null && publicIpAddresses.Count() > 0)
178181
{
179-
this.IpConfigurations.Add(
180-
new PSAzureFirewallIpConfiguration
181-
{
182-
Name = $"{AzureFirewallIpConfigurationName}{i}",
183-
PublicIpAddress = new PSResourceId { Id = publicIpAddresses[i].Id }
184-
});
182+
for (var i = 0; i < publicIpAddresses.Count(); i++)
183+
{
184+
this.IpConfigurations.Add(
185+
new PSAzureFirewallIpConfiguration
186+
{
187+
Name = $"{AzureFirewallIpConfigurationName}{i}",
188+
PublicIpAddress = new PSResourceId { Id = publicIpAddresses[i].Id }
189+
});
190+
}
191+
}
192+
else
193+
{
194+
this.IpConfigurations.Add(new PSAzureFirewallIpConfiguration{Name = $"{AzureFirewallIpConfigurationName}{0}"});
185195
}
186196

187197
this.IpConfigurations[0].Subnet = new PSResourceId { Id = firewallSubnet.Id };
@@ -217,24 +227,33 @@ public void AddPublicIpAddress(PSPublicIpAddress publicIpAddress)
217227
throw new InvalidOperationException($"Please invoke {nameof(Allocate)} to attach the firewall to a Virtual Network");
218228
}
219229

220-
var i = 0;
221-
conflictingIpConfig = null;
222-
var newIpConfigName = "";
230+
PSAzureFirewallIpConfiguration configWithoutIP = this.IpConfigurations.SingleOrDefault
231+
(ipConfig => (ipConfig.Subnet != null && ipConfig.PublicIpAddress == null));
223232

224-
do
233+
if (configWithoutIP != null)
225234
{
226-
newIpConfigName = $"{AzureFirewallIpConfigurationName}{this.IpConfigurations.Count + i}";
227-
conflictingIpConfig = this.IpConfigurations.SingleOrDefault
228-
(ipConfig => string.Equals(ipConfig.Name, newIpConfigName, System.StringComparison.CurrentCultureIgnoreCase));
229-
i++;
230-
} while (conflictingIpConfig != null);
231-
232-
this.IpConfigurations.Add(
233-
new PSAzureFirewallIpConfiguration
235+
configWithoutIP.PublicIpAddress = new PSResourceId { Id = publicIpAddress.Id };
236+
}
237+
else
238+
{
239+
var i = 0;
240+
conflictingIpConfig = null;
241+
var newIpConfigName = "";
242+
do
234243
{
235-
Name = newIpConfigName,
236-
PublicIpAddress = new PSResourceId { Id = publicIpAddress.Id }
237-
});
244+
newIpConfigName = $"{AzureFirewallIpConfigurationName}{this.IpConfigurations.Count + i}";
245+
conflictingIpConfig = this.IpConfigurations.SingleOrDefault
246+
(ipConfig => string.Equals(ipConfig.Name, newIpConfigName, System.StringComparison.CurrentCultureIgnoreCase));
247+
i++;
248+
} while (conflictingIpConfig != null);
249+
250+
this.IpConfigurations.Add(
251+
new PSAzureFirewallIpConfiguration
252+
{
253+
Name = newIpConfigName,
254+
PublicIpAddress = new PSResourceId { Id = publicIpAddress.Id }
255+
});
256+
}
238257
}
239258

240259
public void RemovePublicIpAddress(PSPublicIpAddress publicIpAddress)
@@ -252,21 +271,32 @@ public void RemovePublicIpAddress(PSPublicIpAddress publicIpAddress)
252271
throw new ArgumentException($"Public IP Address {publicIpAddress.Id} is not attached to firewall {this.Name}");
253272
}
254273

255-
if (this.IpConfigurations.Count > 1 && ipConfigToRemove.Subnet != null)
256-
{
257-
throw new InvalidOperationException($"Cannot remove IpConfiguration {ipConfigToRemove.Name} because it references subnet {ipConfigToRemove.Subnet.Id}. Move the subnet reference to another IpConfiguration and try again.");
274+
if (this.ManagementIpConfiguration != null) {
275+
if (ipConfigToRemove.Subnet != null)
276+
{
277+
ipConfigToRemove.PublicIpAddress = null;
278+
}
279+
else
280+
{
281+
this.IpConfigurations.Remove(ipConfigToRemove);
282+
}
258283
}
259-
260-
if (this.IpConfigurations.Count == 1)
284+
else
261285
{
262-
Console.ForegroundColor = ConsoleColor.Yellow;
263-
Console.WriteLine($"WARNING: Removing the last Public IP Address, this will deallocate the firewall. You will have to invoke {nameof(Allocate)} to reallocate it.");
264-
Console.ResetColor();
286+
if (this.IpConfigurations.Count > 1 && ipConfigToRemove.Subnet != null)
287+
{
288+
throw new InvalidOperationException($"Cannot remove IpConfiguration {ipConfigToRemove.Name} because it references subnet {ipConfigToRemove.Subnet.Id}. Move the subnet reference to another IpConfiguration and try again.");
289+
}
265290

266-
this.ManagementIpConfiguration = null;
267-
}
291+
if (this.IpConfigurations.Count == 1)
292+
{
293+
Console.ForegroundColor = ConsoleColor.Yellow;
294+
Console.WriteLine($"WARNING: Removing the last Public IP Address, this will deallocate the firewall. You will have to invoke {nameof(Allocate)} to reallocate it.");
295+
Console.ResetColor();
296+
}
268297

269-
this.IpConfigurations.Remove(ipConfigToRemove);
298+
this.IpConfigurations.Remove(ipConfigToRemove);
299+
}
270300
}
271301

272302
#endregion // Ip Configuration Operations

src/Network/Network/help/New-AzFirewall.md

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ New-AzFirewall -Name <String> -ResourceGroupName <String> -Location <String>
2929
### OldIpConfigurationParameterValues
3030
```
3131
New-AzFirewall -Name <String> -ResourceGroupName <String> -Location <String> -VirtualNetworkName <String>
32-
-PublicIpName <String> [-ApplicationRuleCollection <PSAzureFirewallApplicationRuleCollection[]>]
32+
[-PublicIpName <String>] [-ApplicationRuleCollection <PSAzureFirewallApplicationRuleCollection[]>]
3333
[-NatRuleCollection <PSAzureFirewallNatRuleCollection[]>]
3434
[-NetworkRuleCollection <PSAzureFirewallNetworkRuleCollection[]>] [-ThreatIntelMode <String>]
3535
[-ThreatIntelWhitelist <PSAzureFirewallThreatIntelWhitelist>] [-PrivateRange <String[]>] [-EnableDnsProxy]
@@ -42,7 +42,7 @@ New-AzFirewall -Name <String> -ResourceGroupName <String> -Location <String> -Vi
4242
### IpConfigurationParameterValues
4343
```
4444
New-AzFirewall -Name <String> -ResourceGroupName <String> -Location <String> -VirtualNetwork <PSVirtualNetwork>
45-
-PublicIpAddress <PSPublicIpAddress[]> [-ManagementPublicIpAddress <PSPublicIpAddress>]
45+
[-PublicIpAddress <PSPublicIpAddress[]>] [-ManagementPublicIpAddress <PSPublicIpAddress>]
4646
[-ApplicationRuleCollection <PSAzureFirewallApplicationRuleCollection[]>]
4747
[-NatRuleCollection <PSAzureFirewallNatRuleCollection[]>]
4848
[-NetworkRuleCollection <PSAzureFirewallNetworkRuleCollection[]>] [-ThreatIntelMode <String>]
@@ -242,7 +242,7 @@ $fw=New-AzFirewall -Name "azFw" -ResourceGroupName $rgName -Location westus -Sku
242242
This example creates a Firewall attached to virtual hub "hub" in the same resource group as the firewall.
243243
The Firewall will be assigned 2 public IPs that are created implicitly.
244244

245-
### 16: Create a Firewall with Allow Active FTP.
245+
### Example 16: Create a Firewall with Allow Active FTP.
246246
```
247247
$rgName = "resourceGroupName"
248248
$vnet = Get-AzVirtualNetwork -ResourceGroupName $rgName -Name "vnet"
@@ -252,6 +252,18 @@ New-AzFirewall -Name "azFw" -ResourceGroupName $rgName -Location centralus -Virt
252252

253253
This example creates a Firewall with allow active FTP flag.
254254

255+
### Example 17: Create a Firewall with a management subnet and no data Public IP address
256+
```powershell
257+
$rgName = "resourceGroupName"
258+
$vnet = Get-AzVirtualNetwork -ResourceGroupName $rgName -Name "vnet"
259+
$mgmtPip = Get-AzPublicIpAddress -ResourceGroupName $rgName -Name "managementPublicIpName"
260+
261+
New-AzFirewall -Name "azFw" -ResourceGroupName $rgName -Location centralus -VirtualNetwork $vnet -ManagementPublicIpAddress $mgmtPip
262+
```
263+
264+
This example creates a "forced tunneling" Firewall that uses the subnet "AzureFirewallManagementSubnet" and the management public IP address for its management traffic.
265+
In this scenario, users do not have to specify a data Public IP if they are only using firewall for private traffic only.
266+
255267
## PARAMETERS
256268

257269
### -AllowActiveFTP
@@ -482,14 +494,14 @@ Accept wildcard characters: False
482494
```
483495
484496
### -PublicIpAddress
485-
One or more Public IP Addresses. The Public IP addresses must use Standard SKU and must belong to the same resource group as the Firewall.
497+
One or more Public IP Addresses. The Public IP addresses must use Standard SKU and must belong to the same resource group as the Firewall. No input needed for Forced Tunneling Firewalls.
486498
487499
```yaml
488500
Type: Microsoft.Azure.Commands.Network.Models.PSPublicIpAddress[]
489501
Parameter Sets: IpConfigurationParameterValues
490502
Aliases:
491503

492-
Required: True
504+
Required: False
493505
Position: Named
494506
Default value: None
495507
Accept pipeline input: True (ByPropertyName)
@@ -504,7 +516,7 @@ Type: System.String
504516
Parameter Sets: OldIpConfigurationParameterValues
505517
Aliases:
506518

507-
Required: True
519+
Required: False
508520
Position: Named
509521
Default value: None
510522
Accept pipeline input: True (ByPropertyName)

0 commit comments

Comments
 (0)