Skip to content

Commit cfe083f

Browse files
author
Maddie Clayton
authored
Merge pull request Azure#7230 from fanymanea/AzureFirewallNatAndFqdnTags
[AzureFirewall] NAT and FQDN Tags
2 parents f8d1df4 + 5355c97 commit cfe083f

31 files changed

+2635
-1622
lines changed

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

Lines changed: 279 additions & 218 deletions
Large diffs are not rendered by default.

src/ResourceManager/Network/Commands.Network.Test/SessionRecords/Commands.Network.Test.ScenarioTests.AzureFirewallTests/TestAzureFirewallCRUD.json

Lines changed: 1077 additions & 1321 deletions
Large diffs are not rendered by default.

src/ResourceManager/Network/Commands.Network/Az.Network.psd1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,8 @@ CmdletsToExport = 'Add-AzApplicationGatewayAuthenticationCertificate',
384384
'Remove-AzFirewall',
385385
'New-AzFirewallApplicationRuleCollection',
386386
'New-AzFirewallApplicationRule',
387+
'New-AzFirewallNatRuleCollection',
388+
'New-AzFirewallNatRule',
387389
'New-AzFirewallNetworkRuleCollection',
388390
'New-AzFirewallNetworkRule',
389391
'Get-AzInterfaceEndpoint'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
namespace Microsoft.Azure.Commands.Network
16+
{
17+
public static class AzureFirewallApplicationRuleParameterSets
18+
{
19+
public const string TargetFqdn = @"TargetFqdn";
20+
21+
public const string FqdnTag = @"FqdnTag";
22+
}
23+
}

src/ResourceManager/Network/Commands.Network/AzureFirewall/ApplicationRule/NewAzureFirewallApplicationRuleCommand.cs

Lines changed: 26 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616
using System.Collections.Generic;
1717
using System.Linq;
1818
using System.Management.Automation;
19-
using System.Text.RegularExpressions;
2019
using Microsoft.Azure.Commands.Network.Models;
21-
using MNM = Microsoft.Azure.Management.Network.Models;
2220

2321
namespace Microsoft.Azure.Commands.Network
2422
{
25-
[Cmdlet(VerbsCommon.New, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "FirewallApplicationRule", SupportsShouldProcess = true), OutputType(typeof(PSAzureFirewallApplicationRule))]
23+
[Cmdlet(VerbsCommon.New, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "FirewallApplicationRule", SupportsShouldProcess = true, DefaultParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn), OutputType(typeof(PSAzureFirewallApplicationRule))]
2624
public class NewAzureFirewallApplicationRuleCommand : NetworkBaseCmdlet
2725
{
2826
[Parameter(
2927
Mandatory = true,
28+
ParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn,
29+
HelpMessage = "The name of the Application Rule")]
30+
[Parameter(
31+
Mandatory = true,
32+
ParameterSetName = AzureFirewallApplicationRuleParameterSets.FqdnTag,
3033
HelpMessage = "The name of the Application Rule")]
3134
[ValidateNotNullOrEmpty]
3235
public virtual string Name { get; set; }
@@ -45,20 +48,35 @@ public class NewAzureFirewallApplicationRuleCommand : NetworkBaseCmdlet
4548

4649
[Parameter(
4750
Mandatory = true,
51+
ParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn,
4852
HelpMessage = "The target FQDNs of the rule")]
4953
[ValidateNotNullOrEmpty]
5054
public List<string> TargetFqdn { get; set; }
5155

5256
[Parameter(
5357
Mandatory = true,
58+
ParameterSetName = AzureFirewallApplicationRuleParameterSets.FqdnTag,
59+
HelpMessage = "The FQDN Tags of the rule")]
60+
[ValidateNotNullOrEmpty]
61+
public List<string> FqdnTag { get; set; }
62+
63+
[Parameter(
64+
Mandatory = true,
65+
ParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn,
5466
HelpMessage = "The protocols of the rule")]
5567
[ValidateNotNullOrEmpty]
5668
public List<string> Protocol { get; set; }
57-
69+
5870
public override void Execute()
5971
{
6072
base.Execute();
61-
73+
74+
if (FqdnTag != null)
75+
{
76+
this.Protocol = new List<string> { "http", "https" };
77+
FqdnTag = AzureFirewallFqdnTagHelper.MapUserInputToAllowedFqdnTags(FqdnTag);
78+
}
79+
6280
var protocolsAsWeExpectThem = MapUserProtocolsToFirewallProtocols(Protocol);
6381

6482
var applicationRule = new PSAzureFirewallApplicationRule
@@ -67,63 +85,15 @@ public override void Execute()
6785
Description = this.Description,
6886
SourceAddresses = this.SourceAddress,
6987
Protocols = protocolsAsWeExpectThem,
70-
TargetFqdns = this.TargetFqdn
88+
TargetFqdns = this.TargetFqdn,
89+
FqdnTags = this.FqdnTag
7190
};
7291
WriteObject(applicationRule);
7392
}
7493

7594
private List<PSAzureFirewallApplicationRuleProtocol> MapUserProtocolsToFirewallProtocols(List<string> userProtocols)
7695
{
77-
var protocolRegEx = new Regex("^[hH][tT][tT][pP][sS]?(:[1-9][0-9]*)?$");
78-
79-
var supportedProtocolsAndTheirDefaultPorts = new List<PSAzureFirewallApplicationRuleProtocol>
80-
{
81-
new PSAzureFirewallApplicationRuleProtocol { ProtocolType = MNM.AzureFirewallApplicationRuleProtocolType.Http, Port = 80 },
82-
new PSAzureFirewallApplicationRuleProtocol { ProtocolType = MNM.AzureFirewallApplicationRuleProtocolType.Https, Port = 443 }
83-
};
84-
85-
// User can pass "http", "HTtP" or "hTTp:8080"
86-
var protocolsAsWeExpectThem = this.Protocol.Select(userText =>
87-
{
88-
//The actual validation is performed in NRP. Here we are just trying to map user info to our model
89-
if (!protocolRegEx.IsMatch(userText))
90-
{
91-
throw new ArgumentException($"Invalid protocol {userText}");
92-
}
93-
94-
var userParts = userText.Split(':');
95-
var userProtocolText = userParts[0];
96-
var userPortText = userParts.Length == 2 ? userParts[1] : null;
97-
98-
PSAzureFirewallApplicationRuleProtocol supportedProtocol;
99-
try
100-
{
101-
supportedProtocol = supportedProtocolsAndTheirDefaultPorts.Single(protocol => protocol.ProtocolType.Equals(userProtocolText, StringComparison.InvariantCultureIgnoreCase));
102-
}
103-
catch
104-
{
105-
throw new ArgumentException($"Unsupported protocol {userProtocolText}. Supported protocols are {string.Join(", ", supportedProtocolsAndTheirDefaultPorts.Select(proto => proto.ProtocolType))}", nameof(Protocol));
106-
}
107-
108-
uint port;
109-
if (userPortText == null)
110-
{
111-
// Use default port for this protocol
112-
port = supportedProtocol.Port;
113-
}
114-
else if (!uint.TryParse(userPortText, out port))
115-
{
116-
throw new ArgumentException($"Invalid port {userText}", nameof(Protocol));
117-
}
118-
119-
return new PSAzureFirewallApplicationRuleProtocol
120-
{
121-
ProtocolType = supportedProtocol.ProtocolType,
122-
Port = port
123-
};
124-
}).ToList();
125-
126-
return protocolsAsWeExpectThem;
96+
return userProtocols.Select(PSAzureFirewallApplicationRuleProtocol.MapUserInputToApplicationRuleProtocol).ToList();
12797
}
12898
}
12999
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
using System.Collections.Generic;
17+
using MNM = Microsoft.Azure.Management.Network.Models;
18+
19+
namespace Microsoft.Azure.Commands.Network
20+
{
21+
public static class AzureFirewallNetworkRuleProtocolHelper
22+
{
23+
private static readonly IDictionary<string, string> SupportedNetworkProtocols = new Dictionary<string, string>
24+
{
25+
{ MNM.AzureFirewallNetworkRuleProtocol.Any.ToUpper(), MNM.AzureFirewallNetworkRuleProtocol.Any },
26+
{ MNM.AzureFirewallNetworkRuleProtocol.TCP.ToUpper(), MNM.AzureFirewallNetworkRuleProtocol.TCP },
27+
{ MNM.AzureFirewallNetworkRuleProtocol.UDP.ToUpper(), MNM.AzureFirewallNetworkRuleProtocol.UDP },
28+
};
29+
30+
internal static string MapUserInputToNetworkProtocol(string protocolType)
31+
{
32+
if (protocolType == null)
33+
{
34+
throw new ArgumentException("A protocol must be provided", nameof(protocolType));
35+
}
36+
37+
var userInputKey = protocolType.ToUpper();
38+
if (!SupportedNetworkProtocols.ContainsKey(userInputKey))
39+
{
40+
throw new ArgumentException(
41+
$"Invalid protocol {protocolType}. Accepted values are {string.Join(", ", SupportedNetworkProtocols.Values)}.",
42+
nameof(protocolType));
43+
}
44+
45+
return SupportedNetworkProtocols[userInputKey];
46+
}
47+
}
48+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
using System.Collections.Generic;
17+
using System.Linq;
18+
using System.Management.Automation;
19+
using System.Text.RegularExpressions;
20+
using Microsoft.Azure.Commands.Network.Models;
21+
using MNM = Microsoft.Azure.Management.Network.Models;
22+
23+
namespace Microsoft.Azure.Commands.Network
24+
{
25+
[Cmdlet(VerbsCommon.New, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "FirewallNatRule", SupportsShouldProcess = true), OutputType(typeof(PSAzureFirewallNatRule))]
26+
public class NewAzureFirewallNatRuleCommand : NetworkBaseCmdlet
27+
{
28+
[Parameter(
29+
Mandatory = true,
30+
HelpMessage = "The name of the NAT Rule")]
31+
[ValidateNotNullOrEmpty]
32+
public virtual string Name { get; set; }
33+
34+
[Parameter(
35+
Mandatory = false,
36+
HelpMessage = "The description of the rule")]
37+
[ValidateNotNullOrEmpty]
38+
public string Description { get; set; }
39+
40+
[Parameter(
41+
Mandatory = true,
42+
HelpMessage = "The source addresses of the rule")]
43+
[ValidateNotNullOrEmpty]
44+
public List<string> SourceAddress { get; set; }
45+
46+
[Parameter(
47+
Mandatory = true,
48+
HelpMessage = "The destination addresses of the rule")]
49+
[ValidateNotNullOrEmpty]
50+
public List<string> DestinationAddress { get; set; }
51+
52+
[Parameter(
53+
Mandatory = true,
54+
HelpMessage = "The destination ports of the rule")]
55+
[ValidateNotNullOrEmpty]
56+
public List<string> DestinationPort { get; set; }
57+
58+
[Parameter(
59+
Mandatory = true,
60+
HelpMessage = "The protocols of the rule")]
61+
[ValidateSet(
62+
MNM.AzureFirewallNetworkRuleProtocol.Any,
63+
MNM.AzureFirewallNetworkRuleProtocol.TCP,
64+
MNM.AzureFirewallNetworkRuleProtocol.UDP,
65+
IgnoreCase = false)]
66+
public List<string> Protocol { get; set; }
67+
68+
[Parameter(
69+
Mandatory = true,
70+
HelpMessage = "The translated address for this NAT rule")]
71+
[ValidateNotNullOrEmpty]
72+
public string TranslatedAddress { get; set; }
73+
74+
[Parameter(
75+
Mandatory = true,
76+
HelpMessage = "The translated port for this NAT rule")]
77+
[ValidateNotNullOrEmpty]
78+
public string TranslatedPort { get; set; }
79+
80+
public override void Execute()
81+
{
82+
base.Execute();
83+
84+
// Add some validation based on the type of RuleCollection (SNAT will be supported later)
85+
// if (MNM.AzureFirewallNatRCActionType.Dnat.Equals(ActionType))
86+
{
87+
if (DestinationAddress.Count != 1)
88+
{
89+
throw new ArgumentException("Only one destination address is accepted.", nameof(DestinationAddress));
90+
}
91+
92+
if (DestinationPort.Count != 1)
93+
{
94+
throw new ArgumentException("Only one destination port is accepted.", nameof(DestinationPort));
95+
}
96+
97+
ValidateIsSingleIpNotRange(DestinationAddress.Single());
98+
ValidateIsSingleIpNotRange(TranslatedAddress);
99+
100+
ValidateIsSinglePortNotRange(DestinationPort.Single());
101+
ValidateIsSinglePortNotRange(TranslatedPort);
102+
}
103+
104+
var networkRule = new PSAzureFirewallNatRule
105+
{
106+
Name = this.Name,
107+
Description = this.Description,
108+
Protocols = this.Protocol,
109+
SourceAddresses = this.SourceAddress,
110+
DestinationAddresses = this.DestinationAddress,
111+
DestinationPorts = this.DestinationPort,
112+
TranslatedAddress = this.TranslatedAddress,
113+
TranslatedPort = this.TranslatedPort
114+
};
115+
WriteObject(networkRule);
116+
}
117+
118+
private void ValidateIsSingleIpNotRange(string ipStr)
119+
{
120+
var singleIpRegEx = new Regex("^((\\d){1,3}\\.){3}((\\d){1,3})$");
121+
122+
if (!singleIpRegEx.IsMatch(ipStr))
123+
{
124+
throw new ArgumentException($"Invalid value {ipStr}. Only a single IPv4 value is accepted (e.g. 10.1.2.3).");
125+
}
126+
}
127+
128+
private void ValidateIsSinglePortNotRange(string portStr)
129+
{
130+
uint parsed;
131+
if (!uint.TryParse(portStr, out parsed))
132+
{
133+
throw new ArgumentException($"Invalid value {portStr}. Only a single port value is accepted (e.g. 8080).");
134+
}
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)