From 5e9f62dda4e5654e3db85942ee71cb734e644d5b Mon Sep 17 00:00:00 2001 From: Huaxinli123 <128540154+Huaxinli123@users.noreply.github.com> Date: Tue, 30 May 2023 12:18:06 -0700 Subject: [PATCH 1/4] add support for network interface init --- RELEASE_NOTES.md | 3 + .../resources/network-interface.md | 52 +++++++ .../Builders/Builders.NetworkInterface.fs | 129 ++++++++++++++++ src/Farmer/Farmer.fsproj | 1 + src/Tests/Network.fs | 143 ++++++++++++++++++ 5 files changed, 328 insertions(+) create mode 100644 docs/content/api-overview/resources/network-interface.md create mode 100644 src/Farmer/Builders/Builders.NetworkInterface.fs diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index b8f7ef116..80c8fc47d 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,5 +1,8 @@ Release Notes ============= +## 1.7.23 +* Network Interface: Adds support for network interface creation. +* ## 1.7.22 * AVS: Scripting subresource types. diff --git a/docs/content/api-overview/resources/network-interface.md b/docs/content/api-overview/resources/network-interface.md new file mode 100644 index 000000000..ce24b2260 --- /dev/null +++ b/docs/content/api-overview/resources/network-interface.md @@ -0,0 +1,52 @@ +--- +title: "Network Interface" +chapter: false +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 +[Azure Docs](https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface?tabs=azure-portal) + +* NetworkInterface (`Microsoft.Network/networkInterfaces`) + +#### Builder Keywords + +| Applies To | Keyword | Purpose | +|-|------------------|--------------------------------------------------------------------------------------------| +| networkInterface | name | Name of the network interface resource | +| networkInterface | subnet_prefix | Sets the subnet prefix of the vnet for network interface | +| networkInterface | link_to_vnet | Link to existing vnet or to vnet managed by Farmer | +| 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 | + +#### Example + +```fsharp +#r "nuget:Farmer" +open Farmer +open Farmer.Builders +open Farmer.Builders.NetworkInterface + +arm { + location Location.EastUS + + add_resources + [ + vnet { + name "test-vnet" + add_address_spaces [ "10.0.0.0/16" ] + } + networkInterface { + name "my-network-interface" + subnet_prefix "10.0.100.0/24" + link_to_vnet (virtualNetworks.resourceId "test-vnet") + add_static_ip "10.0.100.10" + accelerated_networking_flag false + ip_forwarding_flag false + } + ] +} +``` \ No newline at end of file diff --git a/src/Farmer/Builders/Builders.NetworkInterface.fs b/src/Farmer/Builders/Builders.NetworkInterface.fs new file mode 100644 index 000000000..6e8e09ce2 --- /dev/null +++ b/src/Farmer/Builders/Builders.NetworkInterface.fs @@ -0,0 +1,129 @@ +module Farmer.Builders.NetworkInterface + +open Farmer +open Farmer.Arm +open Farmer +open Farmer.Builders +open Farmer.Network +open Farmer.Arm.Network + +type NetworkInterfaceConfig = + { + Name: ResourceName + AcceleratedNetworkingflag: FeatureFlag option + IpForwarding: FeatureFlag option + IsPrimary: bool option + VirtualNetwork: LinkedResource option + SubnetPrefix: IPAddressCidr + PrivateIpAddress: string + Tags: Map + } + + interface IBuilder with + member this.ResourceId = networkInterfaces.resourceId this.Name + + member this.BuildResources location = + [ + //vnet + let vnetId = + this.VirtualNetwork + |> Option.defaultWith (fun _ -> raiseFarmer "Must set 'vnet' for network interface") + + //subnet + { + Subnet.Name = ResourceName "networkInterfaceSubnet" + Prefix = IPAddressCidr.format this.SubnetPrefix + VirtualNetwork = Some(vnetId) + NetworkSecurityGroup = None + Delegations = [] + NatGateway = None + ServiceEndpoints = [] + AssociatedServiceEndpointPolicies = [] + PrivateEndpointNetworkPolicies = None + PrivateLinkServiceNetworkPolicies = None + } + + //ipConfig + let subnetIpConfigs = [{ + SubnetName = ResourceName "networkInterfaceSubnet" + LoadBalancerBackendAddressPools = [] + PublicIpAddress = None + PrivateIpAllocation = + match this.PrivateIpAddress with + | "" -> Some(AllocationMethod.DynamicPrivateIp) + | ip -> Some(AllocationMethod.StaticPrivateIp(System.Net.IPAddress.Parse ip)) + Primary = this.IsPrimary + }] + + //network interface + { + Name = this.Name + Location = location + EnableAcceleratedNetworking = this.AcceleratedNetworkingflag |> Option.map (fun f -> f.AsBoolean) + EnableIpForwarding = this.IpForwarding |> Option.map (fun f -> f.AsBoolean) + IpConfigs = subnetIpConfigs + Primary = this.IsPrimary + VirtualNetwork = vnetId + NetworkSecurityGroup = None + Tags = this.Tags + } + ] + +type NetworkInterfaceBuilder() = + member _.Yield _ = + { + Name = ResourceName.Empty + AcceleratedNetworkingflag = None + IpForwarding = None + IsPrimary = None + VirtualNetwork = None + SubnetPrefix = + { + Address = System.Net.IPAddress.Parse("10.0.100.0") + Prefix = 16 + } + PrivateIpAddress = "" + Tags = Map.empty + } + + [] + member _.Name(state: NetworkInterfaceConfig, name: string) = { state with Name = ResourceName name } + + [] + member _.AcceleratedNetworkingflag(state: NetworkInterfaceConfig, flag: bool) = + { state with + AcceleratedNetworkingflag = Some(FeatureFlag.ofBool flag) + } + + [] + member _.IpForwarding(state: NetworkInterfaceConfig, flag: bool) = + { state with + IpForwarding = Some(FeatureFlag.ofBool flag) + } + + [] + member _.IsPrimary(state: NetworkInterfaceConfig, isPrimary: bool) = + { state with + IsPrimary = Some(isPrimary) + } + + // linked to managed vnet created by Farmer and linked by user + [] + member _.LinkToVNetId(state: NetworkInterfaceConfig, vnetId: ResourceId) = + { state with + VirtualNetwork = Some(Managed vnetId) + } + + // linked to external existing vnet + member _.LinkToVNetId(state: NetworkInterfaceConfig, vnetName: string) = + { state with + VirtualNetwork = Some(Unmanaged(virtualNetworks.resourceId (ResourceName vnetName))) + } + + [] + member _.SubnetPrefix(state: NetworkInterfaceConfig, prefix) = { state with SubnetPrefix = IPAddressCidr.parse prefix } + + [] + member _.StaticIpAllocation(state: NetworkInterfaceConfig, addr) = { state with PrivateIpAddress = addr } + +let networkInterface = NetworkInterfaceBuilder() \ No newline at end of file diff --git a/src/Farmer/Farmer.fsproj b/src/Farmer/Farmer.fsproj index 0097736b8..b6df16dbe 100644 --- a/src/Farmer/Farmer.fsproj +++ b/src/Farmer/Farmer.fsproj @@ -180,6 +180,7 @@ + diff --git a/src/Tests/Network.fs b/src/Tests/Network.fs index c81524468..abe72f101 100644 --- a/src/Tests/Network.fs +++ b/src/Tests/Network.fs @@ -6,6 +6,7 @@ open Newtonsoft.Json.Linq open Farmer open Farmer.Arm open Farmer.Builders +open Farmer.Builders.NetworkInterface open Farmer.Network open Microsoft.Rest @@ -828,4 +829,146 @@ let tests = "[resourceId('farmer-pls', 'Microsoft.Network/privateLinkServices', 'pls')]" "Incorrect private link service ID" } + + test "Creates basic network interface with static ip" { + let deployment = + arm { + location Location.EastUS + + add_resources + [ + vnet { + name "test-vnet" + add_address_spaces [ "10.0.0.0/16" ] + } + networkInterface { + name "my-network-interface" + subnet_prefix "10.0.100.0/24" + link_to_vnet (virtualNetworks.resourceId "test-vnet") + add_static_ip "10.0.100.10" + accelerated_networking_flag false + ip_forwarding_flag false + } + ] + } + + let jobj = deployment.Template |> Writer.toJson |> JObject.Parse + let templateStr = jobj.ToString() + + //validate vnet generated + let vnet = + jobj.SelectToken "resources[?(@.type=='Microsoft.Network/virtualNetworks')]" + + Expect.isNotNull vnet "vnet should be generated" + + //validate subnet generated + let subnet = + jobj.SelectToken "resources[?(@.type=='Microsoft.Network/virtualNetworks/subnets')]" + + Expect.isNotNull subnet "subnet should be generated" + + let subnetName = subnet.["name"] + Expect.equal subnetName "test-vnet/networkInterfaceSubnet" "Incorrect default value for subnet name" + + let subnetProps = subnet.["properties"] + let addressPrefix: string = JToken.op_Explicit subnetProps.["addressPrefix"] + Expect.equal addressPrefix "10.0.100.0/24" "Incorrect addressPrefix for subnet" + + //validate network interface generated + let networkInterface = + jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')]" + + Expect.isNotNull networkInterface "network interface should be generated" + + let networkInterfaceName = networkInterface.["name"] + + Expect.equal + networkInterfaceName + "my-network-interface" + "Incorrect default value for network interface name" + + let networkInterfaceDependencies = + jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')].dependsOn" + :?> Newtonsoft.Json.Linq.JArray + + Expect.isNotNull networkInterfaceDependencies "Missing dependency for networkInterface" + Expect.hasLength networkInterfaceDependencies 1 "Incorrect number of dependencies for networkInterface" + + Expect.equal + (networkInterfaceDependencies.[0].ToString()) + "[resourceId(\u0027Microsoft.Network/virtualNetworks\u0027, \u0027test-vnet\u0027)]" + "Incorrect networkInterface dependencies" + + let networkInterfaceProps = networkInterface.["properties"] + + let enableAcceleratedNetworking: bool = + JToken.op_Explicit networkInterfaceProps.["enableAcceleratedNetworking"] + Expect.equal enableAcceleratedNetworking false "Incorrect default value for enableAcceleratedNetworking" + + let enableIPForwarding: bool = + JToken.op_Explicit networkInterfaceProps.["enableIPForwarding"] + Expect.equal enableIPForwarding false "Incorrect default value for enableIPForwarding" + + //validate ip config generated + let ipConfig = networkInterfaceProps.["ipConfigurations"].[0] + Expect.isNotNull ipConfig "network interface ip config should be generated" + + let ipConfigName = ipConfig.["name"] + Expect.equal + ipConfigName + "ipconfig1" + "Incorrect default value for network interface ip config name" + + let ipConfigProps = ipConfig.["properties"] + + let privateIPAddress: string = + JToken.op_Explicit ipConfigProps.["privateIPAddress"] + Expect.equal privateIPAddress "10.0.100.10" "Incorrect default value for privateIPAddress" + + let privateIPAllocationMethod: string = + JToken.op_Explicit ipConfigProps.["privateIPAllocationMethod"] + Expect.equal privateIPAllocationMethod "Static" "Incorrect default value for privateIPAllocationMethod" + + let subnetId = ipConfigProps.SelectToken("subnet.id").ToString() + Expect.equal + subnetId + "[resourceId(\u0027Microsoft.Network/virtualNetworks/subnets\u0027, \u0027test-vnet\u0027, \u0027networkInterfaceSubnet\u0027)]" + "Incorrect subnet id for ipConfig" + } + + test "Creates basic network interface with dynamic ip" { + let deployment = + arm { + location Location.EastUS + + add_resources + [ + vnet { + name "test-vnet" + add_address_spaces [ "10.0.0.0/16" ] + } + networkInterface { + name "my-network-interface" + subnet_prefix "10.0.100.0/24" + link_to_vnet (virtualNetworks.resourceId "test-vnet") + } + ] + } + + let jobj = deployment.Template |> Writer.toJson |> JObject.Parse + let templateStr = jobj.ToString() + + let networkInterface = + jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')]" + Expect.isNotNull networkInterface "network interface should be generated" + + let ipConfig = networkInterface.["properties"].["ipConfigurations"].[0] + Expect.isNotNull ipConfig "network interface ip config should be generated" + + let ipConfigProps = ipConfig.["properties"] + + let privateIPAllocationMethod: string = + JToken.op_Explicit ipConfigProps.["privateIPAllocationMethod"] + Expect.equal privateIPAllocationMethod "Dynamic" "Incorrect default value for privateIPAllocationMethod" + } ] From c0c7f903811fd59782e83413c3d89ae68ebc1422 Mon Sep 17 00:00:00 2001 From: Huaxinli123 <128540154+Huaxinli123@users.noreply.github.com> Date: Tue, 30 May 2023 12:42:17 -0700 Subject: [PATCH 2/4] fantomas --- .../Builders/Builders.NetworkInterface.fs | 46 ++++++++++-------- src/Tests/Network.fs | 48 ++++++++++--------- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/Farmer/Builders/Builders.NetworkInterface.fs b/src/Farmer/Builders/Builders.NetworkInterface.fs index 6e8e09ce2..13a6d4882 100644 --- a/src/Farmer/Builders/Builders.NetworkInterface.fs +++ b/src/Farmer/Builders/Builders.NetworkInterface.fs @@ -42,19 +42,22 @@ type NetworkInterfaceConfig = PrivateEndpointNetworkPolicies = None PrivateLinkServiceNetworkPolicies = None } - + //ipConfig - let subnetIpConfigs = [{ - SubnetName = ResourceName "networkInterfaceSubnet" - LoadBalancerBackendAddressPools = [] - PublicIpAddress = None - PrivateIpAllocation = - match this.PrivateIpAddress with - | "" -> Some(AllocationMethod.DynamicPrivateIp) - | ip -> Some(AllocationMethod.StaticPrivateIp(System.Net.IPAddress.Parse ip)) - Primary = this.IsPrimary - }] - + let subnetIpConfigs = + [ + { + SubnetName = ResourceName "networkInterfaceSubnet" + LoadBalancerBackendAddressPools = [] + PublicIpAddress = None + PrivateIpAllocation = + match this.PrivateIpAddress with + | "" -> Some(AllocationMethod.DynamicPrivateIp) + | ip -> Some(AllocationMethod.StaticPrivateIp(System.Net.IPAddress.Parse ip)) + Primary = this.IsPrimary + } + ] + //network interface { Name = this.Name @@ -78,10 +81,10 @@ type NetworkInterfaceBuilder() = IsPrimary = None VirtualNetwork = None SubnetPrefix = - { - Address = System.Net.IPAddress.Parse("10.0.100.0") - Prefix = 16 - } + { + Address = System.Net.IPAddress.Parse("10.0.100.0") + Prefix = 16 + } PrivateIpAddress = "" Tags = Map.empty } @@ -94,7 +97,7 @@ type NetworkInterfaceBuilder() = { state with AcceleratedNetworkingflag = Some(FeatureFlag.ofBool flag) } - + [] member _.IpForwarding(state: NetworkInterfaceConfig, flag: bool) = { state with @@ -121,9 +124,12 @@ type NetworkInterfaceBuilder() = } [] - member _.SubnetPrefix(state: NetworkInterfaceConfig, prefix) = { state with SubnetPrefix = IPAddressCidr.parse prefix } - + member _.SubnetPrefix(state: NetworkInterfaceConfig, prefix) = + { state with + SubnetPrefix = IPAddressCidr.parse prefix + } + [] member _.StaticIpAllocation(state: NetworkInterfaceConfig, addr) = { state with PrivateIpAddress = addr } -let networkInterface = NetworkInterfaceBuilder() \ No newline at end of file +let networkInterface = NetworkInterfaceBuilder() diff --git a/src/Tests/Network.fs b/src/Tests/Network.fs index abe72f101..3dca7fc74 100644 --- a/src/Tests/Network.fs +++ b/src/Tests/Network.fs @@ -829,7 +829,7 @@ let tests = "[resourceId('farmer-pls', 'Microsoft.Network/privateLinkServices', 'pls')]" "Incorrect private link service ID" } - + test "Creates basic network interface with static ip" { let deployment = arm { @@ -854,8 +854,8 @@ let tests = let jobj = deployment.Template |> Writer.toJson |> JObject.Parse let templateStr = jobj.ToString() - - //validate vnet generated + + //validate vnet generated let vnet = jobj.SelectToken "resources[?(@.type=='Microsoft.Network/virtualNetworks')]" @@ -873,7 +873,7 @@ let tests = let subnetProps = subnet.["properties"] let addressPrefix: string = JToken.op_Explicit subnetProps.["addressPrefix"] Expect.equal addressPrefix "10.0.100.0/24" "Incorrect addressPrefix for subnet" - + //validate network interface generated let networkInterface = jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')]" @@ -900,42 +900,42 @@ let tests = "Incorrect networkInterface dependencies" let networkInterfaceProps = networkInterface.["properties"] - + let enableAcceleratedNetworking: bool = JToken.op_Explicit networkInterfaceProps.["enableAcceleratedNetworking"] + Expect.equal enableAcceleratedNetworking false "Incorrect default value for enableAcceleratedNetworking" - + let enableIPForwarding: bool = JToken.op_Explicit networkInterfaceProps.["enableIPForwarding"] + Expect.equal enableIPForwarding false "Incorrect default value for enableIPForwarding" - + //validate ip config generated let ipConfig = networkInterfaceProps.["ipConfigurations"].[0] Expect.isNotNull ipConfig "network interface ip config should be generated" - + let ipConfigName = ipConfig.["name"] - Expect.equal - ipConfigName - "ipconfig1" - "Incorrect default value for network interface ip config name" - + Expect.equal ipConfigName "ipconfig1" "Incorrect default value for network interface ip config name" + let ipConfigProps = ipConfig.["properties"] - - let privateIPAddress: string = - JToken.op_Explicit ipConfigProps.["privateIPAddress"] + + let privateIPAddress: string = JToken.op_Explicit ipConfigProps.["privateIPAddress"] Expect.equal privateIPAddress "10.0.100.10" "Incorrect default value for privateIPAddress" - + let privateIPAllocationMethod: string = JToken.op_Explicit ipConfigProps.["privateIPAllocationMethod"] + Expect.equal privateIPAllocationMethod "Static" "Incorrect default value for privateIPAllocationMethod" - + let subnetId = ipConfigProps.SelectToken("subnet.id").ToString() + Expect.equal subnetId "[resourceId(\u0027Microsoft.Network/virtualNetworks/subnets\u0027, \u0027test-vnet\u0027, \u0027networkInterfaceSubnet\u0027)]" "Incorrect subnet id for ipConfig" } - + test "Creates basic network interface with dynamic ip" { let deployment = arm { @@ -957,18 +957,20 @@ let tests = let jobj = deployment.Template |> Writer.toJson |> JObject.Parse let templateStr = jobj.ToString() - + let networkInterface = jobj.SelectToken "resources[?(@.type=='Microsoft.Network/networkInterfaces')]" + Expect.isNotNull networkInterface "network interface should be generated" - + let ipConfig = networkInterface.["properties"].["ipConfigurations"].[0] Expect.isNotNull ipConfig "network interface ip config should be generated" - + let ipConfigProps = ipConfig.["properties"] - + let privateIPAllocationMethod: string = JToken.op_Explicit ipConfigProps.["privateIPAllocationMethod"] + Expect.equal privateIPAllocationMethod "Dynamic" "Incorrect default value for privateIPAllocationMethod" } ] From 22e8e95214a90daa9fa77c4eca096748a92a7704 Mon Sep 17 00:00:00 2001 From: Huaxinli123 <128540154+Huaxinli123@users.noreply.github.com> Date: Thu, 1 Jun 2023 16:23:55 -0700 Subject: [PATCH 3/4] resolve comments --- .../resources/network-interface.md | 41 ++++- .../Builders/Builders.NetworkInterface.fs | 142 ++++++++++++------ src/Tests/Network.fs | 22 +-- 3 files changed, 140 insertions(+), 65 deletions(-) diff --git a/docs/content/api-overview/resources/network-interface.md b/docs/content/api-overview/resources/network-interface.md index ce24b2260..e48d8851a 100644 --- a/docs/content/api-overview/resources/network-interface.md +++ b/docs/content/api-overview/resources/network-interface.md @@ -13,14 +13,16 @@ communicate with internet, Azure, and on-premises resources. To learn more about #### Builder Keywords -| Applies To | Keyword | Purpose | -|-|------------------|--------------------------------------------------------------------------------------------| -| networkInterface | name | Name of the network interface resource | -| networkInterface | subnet_prefix | Sets the subnet prefix of the vnet for network interface | -| networkInterface | link_to_vnet | Link to existing vnet or to vnet managed by Farmer | -| 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 | +| Applies To | Keyword | Purpose | +|-|------------------|-------------------------------------------------------------------------------------------------------| +| networkInterface | name | Name of the network interface resource | +| networkInterface | link_to_subnet | Link to existing subnet. If not provided, need to specify the subnet name and prefix for a new subnet | +| networkInterface | subnet_name | Sets the name of the vnet subnet for network interface | +| networkInterface | subnet_prefix | Sets the prefix of the vnet subnet for network interface | +| networkInterface | link_to_vnet | Link to existing vnet or to vnet managed by Farmer | +| 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 | #### Example @@ -41,6 +43,7 @@ arm { } networkInterface { name "my-network-interface" + subnet_name "my-subnet" subnet_prefix "10.0.100.0/24" link_to_vnet (virtualNetworks.resourceId "test-vnet") add_static_ip "10.0.100.10" @@ -49,4 +52,26 @@ arm { } ] } +``` + +#### Example using existing vnet and subnet with dynamic ip allocation + +```fsharp +#r "nuget:Farmer" +open Farmer +open Farmer.Builders +open Farmer.Builders.NetworkInterface + +arm { + location Location.EastUS + + add_resources + [ + networkInterface { + name "my-network-interface" + link_to_subnet "test-subnet" + link_to_vnet "test-vnet" + } + ] +} ``` \ No newline at end of file diff --git a/src/Farmer/Builders/Builders.NetworkInterface.fs b/src/Farmer/Builders/Builders.NetworkInterface.fs index 13a6d4882..2d1b244b2 100644 --- a/src/Farmer/Builders/Builders.NetworkInterface.fs +++ b/src/Farmer/Builders/Builders.NetworkInterface.fs @@ -14,8 +14,10 @@ type NetworkInterfaceConfig = IpForwarding: FeatureFlag option IsPrimary: bool option VirtualNetwork: LinkedResource option - SubnetPrefix: IPAddressCidr - PrivateIpAddress: string + SubnetName: string option + SubnetPrefix: IPAddressCidr option + LinkedSubnet: LinkedResource option + PrivateIpAddress: AllocationMethod Tags: Map } @@ -29,47 +31,79 @@ type NetworkInterfaceConfig = this.VirtualNetwork |> Option.defaultWith (fun _ -> raiseFarmer "Must set 'vnet' for network interface") - //subnet - { - Subnet.Name = ResourceName "networkInterfaceSubnet" - Prefix = IPAddressCidr.format this.SubnetPrefix - VirtualNetwork = Some(vnetId) - NetworkSecurityGroup = None - Delegations = [] - NatGateway = None - ServiceEndpoints = [] - AssociatedServiceEndpointPolicies = [] - PrivateEndpointNetworkPolicies = None - PrivateLinkServiceNetworkPolicies = None - } - - //ipConfig - let subnetIpConfigs = - [ + match this.LinkedSubnet with + | Some subnet -> + //ipConfig + let subnetIpConfigs = + [ + { + SubnetName = subnet.Name + LoadBalancerBackendAddressPools = [] + PublicIpAddress = None + PrivateIpAllocation = Some(this.PrivateIpAddress) + Primary = this.IsPrimary + } + ] + + //network interface + { + Name = this.Name + Location = location + EnableAcceleratedNetworking = + this.AcceleratedNetworkingflag |> Option.map (fun f -> f.AsBoolean) + EnableIpForwarding = this.IpForwarding |> Option.map (fun f -> f.AsBoolean) + IpConfigs = subnetIpConfigs + Primary = this.IsPrimary + VirtualNetwork = vnetId + NetworkSecurityGroup = None + Tags = this.Tags + } + + | None -> + match this.SubnetName, this.SubnetPrefix with + | Some subnetName, Some subnetPrefix -> + //subnet { - SubnetName = ResourceName "networkInterfaceSubnet" - LoadBalancerBackendAddressPools = [] - PublicIpAddress = None - PrivateIpAllocation = - match this.PrivateIpAddress with - | "" -> Some(AllocationMethod.DynamicPrivateIp) - | ip -> Some(AllocationMethod.StaticPrivateIp(System.Net.IPAddress.Parse ip)) + Subnet.Name = ResourceName subnetName + Prefix = IPAddressCidr.format subnetPrefix + VirtualNetwork = Some(vnetId) + NetworkSecurityGroup = None + Delegations = [] + NatGateway = None + ServiceEndpoints = [] + AssociatedServiceEndpointPolicies = [] + PrivateEndpointNetworkPolicies = None + PrivateLinkServiceNetworkPolicies = None + } + + //ipConfig + let subnetIpConfigs = + [ + { + SubnetName = ResourceName subnetName + LoadBalancerBackendAddressPools = [] + PublicIpAddress = None + PrivateIpAllocation = Some(this.PrivateIpAddress) + Primary = this.IsPrimary + } + ] + + //network interface + { + Name = this.Name + Location = location + EnableAcceleratedNetworking = + this.AcceleratedNetworkingflag |> Option.map (fun f -> f.AsBoolean) + EnableIpForwarding = this.IpForwarding |> Option.map (fun f -> f.AsBoolean) + IpConfigs = subnetIpConfigs Primary = this.IsPrimary + VirtualNetwork = vnetId + NetworkSecurityGroup = None + Tags = this.Tags } - ] - - //network interface - { - Name = this.Name - Location = location - EnableAcceleratedNetworking = this.AcceleratedNetworkingflag |> Option.map (fun f -> f.AsBoolean) - EnableIpForwarding = this.IpForwarding |> Option.map (fun f -> f.AsBoolean) - IpConfigs = subnetIpConfigs - Primary = this.IsPrimary - VirtualNetwork = vnetId - NetworkSecurityGroup = None - Tags = this.Tags - } + | _ -> + raiseFarmer + $"subnetName and subnetPrefix must be specified for a new subnet if no existing subnet provided." ] type NetworkInterfaceBuilder() = @@ -80,12 +114,10 @@ type NetworkInterfaceBuilder() = IpForwarding = None IsPrimary = None VirtualNetwork = None - SubnetPrefix = - { - Address = System.Net.IPAddress.Parse("10.0.100.0") - Prefix = 16 - } - PrivateIpAddress = "" + SubnetName = None + SubnetPrefix = None + LinkedSubnet = None + PrivateIpAddress = AllocationMethod.DynamicPrivateIp Tags = Map.empty } @@ -123,13 +155,27 @@ type NetworkInterfaceBuilder() = VirtualNetwork = Some(Unmanaged(virtualNetworks.resourceId (ResourceName vnetName))) } + // create subnet through Farmer. Need to specify subnet_name and subnet_prefix + [] + member _.SubnetName(state: NetworkInterfaceConfig, name) = { state with SubnetName = Some(name) } + [] member _.SubnetPrefix(state: NetworkInterfaceConfig, prefix) = { state with - SubnetPrefix = IPAddressCidr.parse prefix + SubnetPrefix = Some(IPAddressCidr.parse prefix) + } + + // linked to external existing subnet + [] + member _.LinkToSubnet(state: NetworkInterfaceConfig, name: string) = + { state with + LinkedSubnet = Some(Unmanaged(subnets.resourceId (ResourceName name))) } [] - member _.StaticIpAllocation(state: NetworkInterfaceConfig, addr) = { state with PrivateIpAddress = addr } + member _.StaticIpAllocation(state: NetworkInterfaceConfig, addr) = + { state with + PrivateIpAddress = AllocationMethod.StaticPrivateIp(System.Net.IPAddress.Parse addr) + } let networkInterface = NetworkInterfaceBuilder() diff --git a/src/Tests/Network.fs b/src/Tests/Network.fs index 3dca7fc74..a3712b40a 100644 --- a/src/Tests/Network.fs +++ b/src/Tests/Network.fs @@ -843,6 +843,7 @@ let tests = } networkInterface { name "my-network-interface" + subnet_name "my-subnet" subnet_prefix "10.0.100.0/24" link_to_vnet (virtualNetworks.resourceId "test-vnet") add_static_ip "10.0.100.10" @@ -868,7 +869,7 @@ let tests = Expect.isNotNull subnet "subnet should be generated" let subnetName = subnet.["name"] - Expect.equal subnetName "test-vnet/networkInterfaceSubnet" "Incorrect default value for subnet name" + Expect.equal subnetName "test-vnet/my-subnet" "Incorrect default value for subnet name" let subnetProps = subnet.["properties"] let addressPrefix: string = JToken.op_Explicit subnetProps.["addressPrefix"] @@ -932,25 +933,21 @@ let tests = Expect.equal subnetId - "[resourceId(\u0027Microsoft.Network/virtualNetworks/subnets\u0027, \u0027test-vnet\u0027, \u0027networkInterfaceSubnet\u0027)]" + "[resourceId(\u0027Microsoft.Network/virtualNetworks/subnets\u0027, \u0027test-vnet\u0027, \u0027my-subnet\u0027)]" "Incorrect subnet id for ipConfig" } - test "Creates basic network interface with dynamic ip" { + test "Creates basic network interface with existing vnet subnet and dynamic ip" { let deployment = arm { location Location.EastUS add_resources [ - vnet { - name "test-vnet" - add_address_spaces [ "10.0.0.0/16" ] - } networkInterface { name "my-network-interface" - subnet_prefix "10.0.100.0/24" - link_to_vnet (virtualNetworks.resourceId "test-vnet") + link_to_subnet "test-subnet" + link_to_vnet "test-vnet" } ] } @@ -972,5 +969,12 @@ let tests = JToken.op_Explicit ipConfigProps.["privateIPAllocationMethod"] Expect.equal privateIPAllocationMethod "Dynamic" "Incorrect default value for privateIPAllocationMethod" + + let subnetId = ipConfigProps.SelectToken("subnet.id").ToString() + + Expect.equal + subnetId + "[resourceId(\u0027Microsoft.Network/virtualNetworks/subnets\u0027, \u0027test-vnet\u0027, \u0027test-subnet\u0027)]" + "Incorrect subnet id for ipConfig" } ] From 0c9d11dcfb082bcdb9e9d08778644c3d691bd749 Mon Sep 17 00:00:00 2001 From: Dave Curylo Date: Mon, 5 Jun 2023 17:35:29 -0400 Subject: [PATCH 4/4] Update version in RELEASE_NOTES.md --- RELEASE_NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 80c8fc47d..3a95343a3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,8 +1,8 @@ Release Notes ============= -## 1.7.23 + +## 1.7.24 * Network Interface: Adds support for network interface creation. -* ## 1.7.22 * AVS: Scripting subresource types.