Skip to content

Commit

Permalink
Merge pull request #1159 from tanmay-j/addingNsgSupportToNics
Browse files Browse the repository at this point in the history
Adding nsg support to nics
  • Loading branch information
ninjarobot authored Nov 16, 2024
2 parents 3936599 + 9e66df9 commit 89c789a
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 21 deletions.
3 changes: 3 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Release Notes
=============

## 1.9.6
- Network Interface: Support for adding Network Security Group (NSG) to Network Interface (NIC)

## 1.9.5
* Fix an issue with Access Policies that allows non-string sequences to be supplied for user / group lookups.
* Virtual Network: Specify the route table for a subnet.
Expand Down
6 changes: 4 additions & 2 deletions docs/content/api-overview/resources/network-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ weight: 5
---

#### Overview
The `networkInterface` builder allows you to create network interfaces (NIC) so that Azure virtual machine (VM) can
communicate with internet, Azure, and on-premises resources. To learn more about routeServer, reference to
The `networkInterface` builder allows you to create network interfaces (NIC) so that Azure virtual machine (VM) can
communicate with internet, Azure, and on-premises resources. To learn more about routeServer, reference to
[Azure Docs](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface?tabs=azure-portal)

* NetworkInterface (`Microsoft.Network/networkInterfaces`)
Expand All @@ -23,6 +23,8 @@ communicate with internet, Azure, and on-premises resources. To learn more about
| networkInterface | add_static_ip | Use static ip for the network interface. If not provided, ip will be dynamically allocated |
| networkInterface | accelerated_networking_flag | The accelerated networking flag for the network interface. Default is false |
| networkInterface | ip_forwarding_flag | The ip forwarding flag for the network interface. Default is false |
| networkInterface | network_security_group | Specify the network security group from the same deployment. |
| networkInterface | link_to_network_security_group | Specify an existing network security group for this network interface. |

#### Example

Expand Down
3 changes: 2 additions & 1 deletion src/Farmer/Arm/Compute.fs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ type NetworkInterfaceConfiguration = {
EnableIpForwarding: bool option
IpConfigs: IpConfiguration list
VirtualNetwork: LinkedResource
NetworkSecurityGroup: ResourceId option
NetworkSecurityGroup: LinkedResource option
Primary: bool
} with

Expand All @@ -187,6 +187,7 @@ type NetworkInterfaceConfiguration = {
enableIPForwarding = this.EnableIpForwarding |> Option.map box |> Option.defaultValue null
networkSecurityGroup =
this.NetworkSecurityGroup
|> Option.map _.ResourceId
|> Option.map ResourceId.AsIdObject
|> Option.map box
|> Option.defaultValue null
Expand Down
25 changes: 12 additions & 13 deletions src/Farmer/Arm/Network.fs
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ type NetworkInterface = {
EnableIpForwarding: bool option
IpConfigs: IpConfiguration list
VirtualNetwork: LinkedResource
NetworkSecurityGroup: ResourceId option
NetworkSecurityGroup: LinkedResource option
Primary: bool option
Tags: Map<string, string>
} with
Expand All @@ -752,8 +752,9 @@ type NetworkInterface = {
match linkedResource with
| Managed resId -> resId
| _ -> ()
if this.NetworkSecurityGroup.IsSome then
this.NetworkSecurityGroup.Value
match this.NetworkSecurityGroup with
| Some(Managed id) -> id
| _ -> ()
]

let props = {|
Expand All @@ -763,20 +764,18 @@ type NetworkInterface = {
ipConfigurations =
this.IpConfigs
|> List.mapi (fun index ipConfig -> ipConfig.ToArmJson(index, this.VirtualNetwork.ResourceId, true))
networkSecurityGroup =
this.NetworkSecurityGroup
|> Option.map (fun nsg -> {|
id = nsg.ResourceId.ArmExpression.Eval()
|})
|> Option.defaultValue Unchecked.defaultof<_>
|}

match this.NetworkSecurityGroup with
| None -> {|
{|
networkInterfaces.Create(this.Name, this.Location, dependsOn, this.Tags) with
properties = props
|}
| Some nsg -> {|
networkInterfaces.Create(this.Name, this.Location, dependsOn, this.Tags) with
properties = {|
props with
networkSecurityGroup = {| id = nsg.Eval() |}
|}
|}
|}

type NetworkProfile = {
Name: ResourceName
Expand Down
40 changes: 38 additions & 2 deletions src/Farmer/Builders/Builders.NetworkInterface.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type NetworkInterfaceConfig = {
SubnetName: string option
SubnetPrefix: IPAddressCidr option
LinkedSubnet: LinkedResource option
NetworkSecurityGroup: LinkedResource option
PrivateIpAddress: AllocationMethod
PrivateIpAddressVersion: AddressVersion
Tags: Map<string, string>
Expand Down Expand Up @@ -56,7 +57,7 @@ type NetworkInterfaceConfig = {
IpConfigs = subnetIpConfigs
Primary = this.IsPrimary
VirtualNetwork = vnetId
NetworkSecurityGroup = None
NetworkSecurityGroup = this.NetworkSecurityGroup
Tags = this.Tags
}

Expand Down Expand Up @@ -102,7 +103,7 @@ type NetworkInterfaceConfig = {
IpConfigs = subnetIpConfigs
Primary = this.IsPrimary
VirtualNetwork = vnetId
NetworkSecurityGroup = None
NetworkSecurityGroup = this.NetworkSecurityGroup
Tags = this.Tags
}
| _ ->
Expand All @@ -120,6 +121,7 @@ type NetworkInterfaceBuilder() =
SubnetName = None
SubnetPrefix = None
LinkedSubnet = None
NetworkSecurityGroup = None
PrivateIpAddress = AllocationMethod.DynamicPrivateIp
PrivateIpAddressVersion = IPv4
Tags = Map.empty
Expand Down Expand Up @@ -195,6 +197,40 @@ type NetworkInterfaceBuilder() =
| _ -> IPv4
}

/// Sets the network security group for network interface
[<CustomOperation "network_security_group">]
member _.NetworkSecurityGroup(state: NetworkInterfaceConfig, nsg: IArmResource) = {
state with
NetworkSecurityGroup = Some(Managed nsg.ResourceId)
}

member _.NetworkSecurityGroup(state: NetworkInterfaceConfig, nsg: ResourceId) = {
state with
NetworkSecurityGroup = Some(Managed nsg)
}

member _.NetworkSecurityGroup(state: NetworkInterfaceConfig, nsg: NsgConfig) = {
state with
NetworkSecurityGroup = Some(Managed (nsg :> IBuilder).ResourceId)
}

/// Links the network interface to an existing network security group.
[<CustomOperation "link_to_network_security_group">]
member _.LinkToNetworkSecurityGroup(state: NetworkInterfaceConfig, nsg: IArmResource) = {
state with
NetworkSecurityGroup = Some(Unmanaged(nsg.ResourceId))
}

member _.LinkToNetworkSecurityGroup(state: NetworkInterfaceConfig, nsg: ResourceId) = {
state with
NetworkSecurityGroup = Some(Unmanaged nsg)
}

member _.LinkToNetworkSecurityGroup(state: NetworkInterfaceConfig, nsg: NsgConfig) = {
state with
NetworkSecurityGroup = Some(Unmanaged (nsg :> IBuilder).ResourceId)
}

interface ITaggable<NetworkInterfaceConfig> with
member _.Add state tags = {
state with
Expand Down
6 changes: 4 additions & 2 deletions src/Farmer/Builders/Builders.Vm.fs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ type VmConfig = {
Prefixes = [ this.SubnetPrefix ]
VirtualNetwork = Some(Managed vnet)
RouteTable = None
NetworkSecurityGroup = nsgId |> Option.map Managed
NetworkSecurityGroup = nsgId
Delegations = []
NatGateway = None
ServiceEndpoints = []
Expand All @@ -176,7 +176,9 @@ type VmConfig = {
member this.ResourceId = this.ResourceId

member this.BuildResources location =
let nsgId = this.NetworkSecurityGroup |> Option.map (fun nsg -> nsg.ResourceId)
let nsgId =
this.NetworkSecurityGroup |> Option.map (fun nsg -> (Managed nsg.ResourceId))

let generatedNics = this.buildNics (location, nsgId)

[
Expand Down
3 changes: 2 additions & 1 deletion src/Farmer/Builders/Builders.VmScaleSet.fs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,8 @@ type VmScaleSetConfig = {
match this.Vm with
| None -> raiseFarmer "The 'vm_profile' must be set for the VM scale set."
| Some vm ->
let nsgId = vm.NetworkSecurityGroup |> Option.map (fun lr -> lr.ResourceId)
let nsgId =
vm.NetworkSecurityGroup |> Option.map (fun lr -> (Managed lr.ResourceId))

[
// The VM Scale Set
Expand Down
76 changes: 76 additions & 0 deletions src/Tests/Network.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1174,6 +1174,82 @@ let tests =
"Incorrect subnet id for ipConfig"
}

test "Creates basic network interface with existing vnet subnet and network security group" {
let deployment = arm {
location Location.EastUS

add_resources [
nsg { name "my-nsg" }
networkInterface {
name "my-network-interface-with-nsg"
link_to_subnet "test-subnet"
link_to_vnet "test-vnet"
network_security_group (networkSecurityGroups.resourceId "my-nsg")
}
]
}

let jobj = deployment.Template |> Writer.toJson |> JObject.Parse

let networkInterfaceWithNsg =
jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')]"

Expect.equal
(networkInterfaceWithNsg.["properties"].["networkSecurityGroup"].["id"]
.ToString())
"[resourceId(\u0027Microsoft.Network/networkSecurityGroups\u0027, \u0027my-nsg\u0027)]"
"Incorrect networkSecurityGroup for networkInterface"

let networkInterfaceWithNsgDependencies =
jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')].dependsOn" :?> JArray

Expect.isNotNull networkInterfaceWithNsgDependencies "Missing dependency for networkInterface"

Expect.hasLength
networkInterfaceWithNsgDependencies
1
"Incorrect number of dependencies for networkInterface"

Expect.equal
(networkInterfaceWithNsgDependencies.[0].ToString())
"[resourceId(\u0027Microsoft.Network/networkSecurityGroups\u0027, \u0027my-nsg\u0027)]"
"Incorrect networkInterface dependencies"
}

test "Creates basic network interface with existing vnet subnet and existing network security group" {
let deployment = arm {
location Location.EastUS

add_resources [
networkInterface {
name "my-network-interface-with-existing-nsg"
link_to_subnet "test-subnet"
link_to_vnet "test-vnet"
link_to_network_security_group (networkSecurityGroups.resourceId "my-nsg")
}
]
}

let jobj = deployment.Template |> Writer.toJson |> JObject.Parse

let networkInterfaceWithExistingNsg =
jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')]"

Expect.equal
(networkInterfaceWithExistingNsg.["properties"].["networkSecurityGroup"].["id"]
.ToString())
"[resourceId(\u0027Microsoft.Network/networkSecurityGroups\u0027, \u0027my-nsg\u0027)]"
"Incorrect networkSecurityGroup for networkInterface"

let networkInterfaceWithExistingNsgDependencies =
jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')].dependsOn" :?> JArray

Expect.hasLength
networkInterfaceWithExistingNsgDependencies
0
"Incorrect number of dependencies for networkInterface"
}

test "Creates basic network interface with existing vnet subnet and dynamic ip" {
let deployment = arm {
location Location.EastUS
Expand Down

0 comments on commit 89c789a

Please sign in to comment.