Public/Deploy/IaaS/networking/New-CmAzIaasNetworking.ps1

function New-CmAzIaasNetworking {

    <#
        .Synopsis
         Creates networking solution
 
        .Description
         Completes following:
            * Creates vnets and subnets. Optionally attach nsg and route tables to subnet.
            * Creates route tables and routes.
            * Creates network security groups.
            * Creates resource groups if doesn't exist.
            * Configure resources in mulitple resource groups at once.
            * Ability to optionally configure networking component independently.
 
        .Parameter SettingsFile
         File path for the settings file to be converted into a settings object.
 
        .Parameter VnetsCsvFile
         File path for the csv containing virtual network configurations.
         Required headers: resourceGroupName|location(optional)|vnetName|addressSpace|subnetName|cidr|networkSecurityGroup|routeTable.
 
        .Parameter RouteTablesCsvFile
         File path for the csv containing route table configurations.
         Required headers: resourceGroupName|location(optional)|tableName|routeName|cidr|nextHopType|nextHopIpAddress|notes
 
        .Parameter NsgsCsvFile
         File path for the csv containing virtual network security group configurations.
         Required headers: resourceGroupName|location(optional)|nsgName|ruleName|priority|direction|sourceIp|sourcePort|destinationIp|destinationPort|protocol|Access|Description
 
        .Parameter ResourceGroupsCsvFile
         File path for the csv containing resource Group and location mapping. By default location of first vnet is used to create resource group.
         Required headers: resourceGroupName|location
 
        .Parameter TagSettingsFile
          File path for settings containing tags definition.
 
        .Component
         IaaS
 
        .Example
         New-CmAzIaasNetworking -settingsFile "networking.yml"
 
        .Example
         New-CmAzIaasNetworking -VnetsCsvFile "vnet.csv" -RouteTablesCsvFile "routeTable.csv" -NsgsCsvFile "nsg.csv" -ResourceGroupCsvFile resourceGroup.csv -Confirm:$false
 
        .Example
         New-CmAzIaasNetworking -VnetsCsvFile "vnet.csv" -RouteTablesCsvFile "routeTable.csv" -NsgsCsvFile "nsg.csv" -Confirm:$false
 
        .Example
         New-CmAzIaasNetworking -RouteTablesCsvFile "routeTable.csv" -Confirm:$false
 
    #>


    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = "High")]
    param(
        [parameter(Mandatory = $true, ParameterSetName = "Settings Yml File")]
        [String]$SettingsFile,
        [parameter(Mandatory = $false, ParameterSetName = "Settings CSV File")]
        [String]$VnetsCsvFile,
        [parameter(Mandatory = $false, ParameterSetName = "Settings CSV File")]
        [String]$RouteTablesCsvFile,
        [parameter(Mandatory = $false, ParameterSetName = "Settings CSV File")]
        [String]$NsgsCsvFile,
        [parameter(Mandatory = $false, ParameterSetName = "Settings CSV File")]
        [String]$ResourceGroupsCsvFile,
        [String]$TagSettingsFile
    )

    $ErrorActionPreference = "Stop"

    try {

        Get-InvocationInfo -CommandName $MyInvocation.MyCommand.Name

        if ($PSCmdlet.ShouldProcess((Get-CmAzSubscriptionName), "Create networking solution")) {

            if ($SettingsFile -and !$SettingsObject -and !$VnetsCsvFile -and !$RouteTablesCsvFile -and !$NsgsCsvFile ) {
                Write-Verbose "Importing setting from Yml file"
                $SettingsObject = Get-CmAzSettingsFile -Path $SettingsFile
            }
            elseif (!$SettingsFile -and !$SettingsObject -and !$VnetsCsvFile -and !$RouteTablesCsvFile -and !$NsgsCsvFile ) {
                Write-Error "No valid input settings." -Category InvalidArgument -CategoryTargetName "SettingsObject"
            }

            $resourceGroupObjectArray = [System.Collections.ArrayList]@()

            # Code to Create Object from CSV

            if ($VnetsCsvFile -or $RouteTablesCsvFile -or $NsgsCsvFile -and !$SettingsObject)    {

                if ($VnetsCsvFile) {
                    Write-Verbose "Vnet CSV Found."
                    $VnetsCsvFile = Resolve-FilePath -NestedFile $VnetsCsvFile
                    $vnetObjectFile = Import-Csv -Path $VnetsCsvFile

                    if ($vnetObjectFile.count -eq 1) {
                        $vnetFile = @($vnetObjectFile)
                    }
                    else {
                        $vnetFile = $vnetObjectFile
                    }
                }
                else {
                    Write-Verbose "Vnet CSV not found."
                    $vnetFile = @()
                }

                if ($RouteTablesCsvFile) {
                    Write-Verbose "Route Table CSV Found."
                    $RouteTablesCsvFile = Resolve-FilePath -NestedFile $RouteTablesCsvFile
                    $routeTablesFile = Import-Csv -Path $RouteTablesCsvFile
                }
                else {
                    Write-Verbose "Route Table CSV not Found."
                    $routeTablesFile = @()
                }

                if ($NsgsCsvFile) {
                    Write-Verbose "Nsg CSV Found."
                    $NsgsCsvFile = Resolve-FilePath -NestedFile $NsgsCsvFile
                    $nsgFile = Import-Csv -Path $NsgsCsvFile
                }
                else {
                    Write-Verbose "Nsg CSV Not Found."
                    $nsgFile = @()
                }

                if ($ResourceGroupsCsvFile) {
                    Write-Verbose "Resource Group CSV Found."
                    $ResourceGroupsCsvFile = Resolve-FilePath -NestedFile $ResourceGroupsCsvFile
                    $resourceGroupslocation = Import-Csv -Path $ResourceGroupsCsvFile
                }

                Write-Verbose "Starting file merge."
                [System.Collections.ArrayList]$mergedFile = $vnetFile + $nsgFile + $routeTablesFile

                if ($nsgFile) {

                    $nsgCsv = $mergedFile | Group-Object nsgName

                    foreach ($nsg in ($nsgCsv | Where-Object { $_.name -like "*.csv" } )) {
                        Write-Verbose "NSG: external CSV detected"

                        foreach ($externalNsg in $nsg.Group) {

                            $interimNsgCsvPath = "$(Split-Path $NsgsCsvFile)/$($nsg.Name)"
                            $interimNsgCsvPath | Write-Verbose
                            $interimNsgFile = Import-Csv -Path $interimNsgCsvPath
                            $interimNsg = $interimNsgFile | Group-Object nsgName
                            $interimNsgObjectArray = [System.Collections.ArrayList]@()

                            foreach ($internalNsg in $interimNsg.Group) {

                                $interimNsgObject = New-Object -TypeName psobject -Property @{
                                    "resourceGroupName"           = $internalNsg.resourceGroupName;
                                    "nsgName"                     = $internalNsg.nsgName;
                                    "location"                    = $internalNsg.location;
                                    "servicePublish"              = $internalNsg.servicePublish;
                                    "resourceGroupServicePublish" = $internalNsg.resourceGroupServicePublish;
                                    "storageServiceDependency"    = $internalNsg.storageServiceDependency;
                                    "workspaceServiceDependency"  = $internalNsg.workspaceServiceDependency;
                                    "ruleName"                    = $externalNsg.ruleName;
                                    "priority"                    = $externalNsg.priority;
                                    "direction"                   = $externalNsg.direction;
                                    "sourceIp"                    = $externalNsg.sourceIp;
                                    "sourcePort"                  = $externalNsg.sourcePort;
                                    "destinationIp"               = $externalNsg.destinationIp;
                                    "destinationPort"             = $externalNsg.destinationPort;
                                    "protocol"                    = $externalNsg.protocol;
                                    "Access"                      = $externalNsg.Access;
                                    "Description"                 = $externalNsg.Description
                                }

                                $interimNsgObjectArray.add($interimNsgObject) > $Null
                            }

                            Write-Verbose "Starting merge.."
                            $mergedFile.Remove($externalNsg)
                            $mergedFile += $interimNsgObjectArray
                        }
                    }
                }

                $resourceGroupsFile = $mergedFile | Group-Object -Property resourceGroupName

                # Code to add vnet in the object
                function subnetObject {
                    param(
                        [Object]$subnetGroup,
                        [String]$subnetName
                    )

                    $subnetObject = @{
                        subnetName           = $subnetName;
                        cidr                 = $subnetGroup.cidr;
                        networkSecurityGroup = $subnetGroup.networkSecurityGroup;
                        routeTable           = $subnetGroup.routeTable;
                    }
                    $subnetObject
                }

                function vnetObject {
                    param(
                        [Object]$vnetGroup,
                        [String]$vnetName
                    )

                    $subnetObjectList = [System.Collections.ArrayList]@()
                    $subnetGroup = $vnetGroup | Group-Object subnetName

                    foreach ($subnet in $subnetGroup) {
                        $subnetObject = subnetObject -subnetGroup $subnet.Group -subnetName $subnet.Name
                        $subnetObjectList.Add($subnetObject) > $Null
                    }

                    if ($vnetGroup.addressSpace -is [array]) {
                        $addressSpace = $vnetGroup.addressSpace[0].ToString().split('|')
                    }
                    else {
                        $addressSpace = $vnetGroup.addressSpace.ToString().split('|')
                    }

                    if ($vnetGroup.dnsServers -and $vnetGroup.dnsServers -ne '') {

                        if ($vnetGroup.dnsServers -is [array]) {
                            $dnsServers = $vnetGroup.dnsServers[0].ToString().split('|')
                        }
                        else {
                            $dnsServers = $vnetGroup.dnsServers.ToString().split('|')
                        }
                    }
                    else {
                        $dnsServers = ""
                    }

                    if ($vnetGroup.virtualnetworkpeers -and $vnetGroup.virtualnetworkpeers -ne '') {

                        if ($vnetGroup.virtualnetworkpeers -is [array]) {
                            $virtualnetworkpeers = $vnetGroup.virtualnetworkpeers[0].ToString().split('|')
                        }
                        else {
                            $virtualnetworkpeers = $vnetGroup.virtualnetworkpeers.ToString().split('|')
                        }
                    }
                    else {
                        $virtualnetworkpeers = @()
                    }

                    if ($vnetGroup.location -is [array]) {
                        $location = $vnetGroup.location[0]
                    }
                    else {
                        $location = $vnetGroup.location
                    }

                    if ($vnetGroup.servicePublish -is [array]) {
                        $servicePublish = $vnetGroup.servicePublish[0]
                    }
                    else {
                        $servicePublish = $vnetGroup.servicePublish
                    }

                    if ($vnetGroup.resourceGroupServicePublish -is [array]) {
                        $resourceGroupServicePublish = $vnetGroup.resourceGroupServicePublish[0]
                    }
                    else {
                        $resourceGroupServicePublish = $vnetGroup.resourceGroupServicePublish
                    }

                    $vnetObject = @{
                        vnetName            = $vnetName;
                        addressSpace        = $addressSpace;
                        dnsServers          = $dnsServers;
                        subnets             = $subnetObjectList;
                        location            = $location;
                        virtualnetworkpeers = $virtualnetworkpeers
                        service             = @{
                            publish = @{
                                vnet          = $servicePublish;
                                resourceGroup = $resourceGroupServicePublish
                            }
                        }
                    }

                    $vnetObject
                }

                # Code to add UDR in the object
                function routeObject {
                    param(
                        [Object]$routeGroup,
                        [String]$routeName
                    )

                    $routeObject = @{
                        routeName        = $routeName;
                        cidr             = $routeGroup.cidr;
                        nextHopType      = $routeGroup.nextHopType;
                        nextHopIpAddress = $routeGroup.nextHopIpAddress
                    }

                    $routeObject
                }
                function routeTableObject {
                    param(
                        [Object]$routeTableGroup,
                        [String]$tableName
                    )

                    $routeObjectList = [System.Collections.ArrayList]@()
                    $routeGroup = $routeTableGroup | Group-Object routeName

                    foreach ($route in $routeGroup) {
                        $routeObject = routeObject -routeGroup $route.Group -routeName $route.Name
                        $routeObjectList.Add($routeObject) > $Null
                    }

                    if ($routeTableGroup.location -is [array]) {
                        $location = $routeTableGroup.location[0]
                    }
                    else {
                        $location = $routeTableGroup.location
                    }

                    if ($routeTableGroup.routePropagation -is [array]) {
                        $routePropagation = $routeTableGroup.routePropagation[0]
                    }
                    else {
                        $routePropagation = $routeTableGroup.routePropagation
                    }

                    [bool]::TryParse($routePropagation, [ref]$routePropagation)  > $Null

                    if ($routeTableGroup.servicePublish -is [array]) {
                        $servicePublish = $routeTableGroup.servicePublish[0]
                    }
                    else {
                        $servicePublish = $routeTableGroup.servicePublish
                    }

                    if ($routeTableGroup.resourceGroupServicePublish -is [array]) {
                        $resourceGroupServicePublish = $routeTableGroup.resourceGroupServicePublish[0]
                    }
                    else {
                        $resourceGroupServicePublish = $routeTableGroup.resourceGroupServicePublish
                    }

                    $routeTableObject = @{
                        tableName = $tableName;
                        routePropagation = $routePropagation;
                        routes    = $routeObjectList;
                        location  = $location;
                        service   = @{
                            publish = @{
                                routeTable    = $servicePublish;
                                resourceGroup = $resourceGroupServicePublish
                            }
                        }
                    }

                    $routeTableObject
                }

                # Code to add Nsg rules in the object
                function nsgruleObject {
                    param(
                        [Object]$nsgGroupObject
                    )

                    $nsgruleObject = @{
                        ruleName        = $nsgGroupObject.ruleName;
                        Description     = $nsgGroupObject.Description;
                        priority        = $nsgGroupObject.priority;
                        direction       = $nsgGroupObject.direction;
                        sourceIp        = $nsgGroupObject.sourceIp.ToString().split(',');
                        sourcePort      = $nsgGroupObject.sourcePort.ToString().split(',');
                        destinationIp   = $nsgGroupObject.destinationIp.ToString().split(',');
                        destinationPort = $nsgGroupObject.destinationPort.ToString().split(',');
                        protocol        = $nsgGroupObject.protocol;
                        Access          = $nsgGroupObject.Access
                    }

                    $nsgruleObject
                }

                function nsgObject {
                    param(
                        [Object]$nsgGroup,
                        [String]$nsgName
                    )

                    $nsgruleObjectList = [System.Collections.ArrayList]@()
                    $nsgGroupObject = $nsgGroup | Group-Object ruleName

                    foreach ($nsg in $nsgGroupObject) {

                        if ($nsg -is [array]) {
                            Write-Error "Rule name not unique : '$($nsg.Name) in '$($nsg.Group.nsgName[0])' for Resource Group: '$($nsg.Group.resourceGroupName[0])'" -CategoryTargetName $nsg.Name -ErrorAction Stop
                        }
                        $nsgruleObject = nsgruleObject -nsgGroupObject $nsg.Group
                        $nsgruleObjectList.Add($nsgruleObject) > $Null
                    }

                    if ($nsgGroup.location -is [array]) {
                        $location = $nsgGroup.location[0]
                    }
                    else {
                        $location = $nsgGroup.location
                    }

                    if ($nsgGroup.servicePublish -is [array]) {
                        $servicePublish = $nsgGroup.servicePublish[0]
                    }
                    else {
                        $servicePublish = $nsgGroup.servicePublish
                    }

                    if ($nsgGroup.resourceGroupServicePublish -is [array]) {
                        $resourceGroupServicePublish = $nsgGroup.resourceGroupServicePublish[0]
                    }
                    else {
                        $resourceGroupServicePublish = $nsgGroup.resourceGroupServicePublish
                    }

                    if ($nsgGroup.storageServiceDependency -is [array]) {
                        $storageServiceDependency = $nsgGroup.storageServiceDependency[0]
                    }
                    else {
                        $storageServiceDependency = $nsgGroup.storageServiceDependency
                    }

                    if ($nsgGroup.workspaceServiceDependency -is [array]) {
                        $workspaceServiceDependency = $nsgGroup.workspaceServiceDependency[0]
                    }
                    else {
                        $workspaceServiceDependency = $nsgGroup.workspaceServiceDependency
                    }

                    $nsgObject = @{
                        nsgName  = $nsgName;
                        rules    = $nsgruleObjectList;
                        location = $location;
                        service  = @{
                            publish      = @{
                                networkSecurityGroup = $servicePublish;
                                resourceGroup        = $resourceGroupServicePublish
                            };
                            dependencies = @{
                                storage   = $storageServiceDependency;
                                workspace = $workspaceServiceDependency;
                            }
                        }
                    }

                    $nsgObject
                }

                # Create a unified Resource Group collection
                foreach ($ResourceGroup in ($resourceGroupsFile | Where-Object { $_.Name -notlike '' })) {

                    if ($ResourceGroup.Name) {

                        Write-Verbose "working for $($ResourceGroup.Name)"

                        # Adding Vnets
                        if ($VnetsCsvFile) {

                            $vnetsGroup = $ResourceGroup.Group | Group-Object vnetName

                            if (!$vnetsGroup.name) {
                                Write-Error "Problem found in `"$($VnetsCsvFile)`". No route tables found. Please check the file." -Category ObjectNotFound -TargetObject $vnetsGroup
                            }

                            Write-Verbose "'$($ResourceGroup.Name)' has vnets = '$($($vnetsGroup.Name -notlike '').count)'"
                            $vnetObjectArray = [System.Collections.ArrayList]@()

                            foreach ($vnet in $vnetsGroup) {

                                if ($vnet.name) {
                                    $vnetObject = vnetObject -vnetGroup $vnet.Group -vnetName $vnet.Name
                                    Write-Verbose "Adding vnet = '$($vnet.Name)' to RG = '$($ResourceGroup.Name)'"
                                    $vnetObjectArray.Add($vnetObject) > $Null
                                }
                            }
                        }

                        # Adding UDR
                        if ($RouteTablesCsvFile) {

                            $routeTableGroup = $ResourceGroup.Group | Group-Object tableName

                            if (!$routeTableGroup.name) {
                                Write-Error "Problem found in `"$($RouteTablesCsvFile)`". No route tables found. Please check the file." -Category ObjectNotFound -TargetObject $routeTableGroup
                            }

                            Write-Verbose "'$($ResourceGroup.Name)' has route tables = '$($($routeTableGroup.Name -notlike '').count)'"
                            $routeTableObjectArray = [System.Collections.ArrayList]@()

                            foreach ($routeTable in $routeTableGroup) {

                                if ($routeTable.name) {
                                    $routeTableObject = routeTableObject -routeTableGroup $routeTable.Group -tableName $routeTable.Name
                                    Write-Verbose "Adding UDR = '$($routeTable.Name)' to RG = '$($ResourceGroup.Name)'"
                                    $routeTableObjectArray.Add($routeTableObject) > $Null
                                }
                            }
                        }

                        # Adding NSG
                        if ($NsgsCsvFile) {

                            $nsgGroup = $ResourceGroup.Group | Group-Object nsgName

                            if (!$nsgGroup.name) {
                                Write-Error "Problem found in `"$($NsgsCsvFile)`". No network security groups found. Please check the file." -Category ObjectNotFound -TargetObject $nsgGroup
                            }

                            Write-Verbose "'$($ResourceGroup.Name)' has network security groups = '$($($nsgGroup.Name -notlike '').count)'"
                            $nsgObjectArray = [System.Collections.ArrayList]@()

                            foreach ($nsg in $nsgGroup) {

                                if ($nsg.name) {
                                    $nsgObject = nsgObject -nsgGroup $nsg.Group -nsgName $nsg.Name
                                    Write-Verbose "Adding nsg = '$($nsg.Name)' to RG = '$($ResourceGroup.Name)'"
                                    $nsgObjectArray.Add($nsgObject) > $Null
                                }
                            }
                        }

                        # Default values if required for ARM template sanity checks
                        if (!$vnetObjectArray) {
                            $vnetObjectArray = @(@{vnetName = "none"; vnetPeerings = @(); location = ""; dnsServers = ""; addressSpace = @("10.10.0.0/24"); subnets = @(@{subnetName = "none"; cidr = "0.0.0.0/0" }); service = @{publish = @{vnet = "" } } })
                        }

                        if (!$routeTableObjectArray) {
                            $routeTableObjectArray = @(@{tableName = "none"; location = ""; routes = @(@{routeName = "none"; cidr = "0.0.0.0/0"; nextHopType = "VirtualAppliance"; nextHopIpAddress = "10.10.10.10" }); service = @{publish = @{routeTable = "" } } })
                        }

                        if (!$nsgObjectArray) {
                            $nsgObjectArray = @(@{nsgName = "none"; location = ""; rules = @(@{ruleName = "none"; description = "none"; priority = "none"; direction = "none"; sourceIp = "10.10.10.10"; sourcePort = 3389; destinationIp = "10.10.10.11"; destinationPort = 3389; protocol = "Tcp"; Access = "allow" }); service = @{publish = @{networkSecurityGroup = "" } } })
                        }

                        # Build Resource Group Config
                        $ifResourceGroupExists = Get-AzResourceGroup -Name $ResourceGroup.Name -ErrorAction SilentlyContinue

                        if (!$ifResourceGroupExists) {

                            $RGlocation = ($resourceGroupslocation | Where-Object { $_.resourceGroupName -like $ResourceGroup.Name }).location
                            $resourceGroupServicePublish = ($resourceGroupslocation | Where-Object { $_.resourceGroupName -like $ResourceGroup.Name }).resourceGroupServicePublish

                            if (!$RGlocation -and $vnetObjectArray[0].location ) {
                                $RGlocation = $vnetObjectArray[0].location
                            }

                            if (!$RGlocation) {
                                Write-Error "Resource Group $($ResourceGroup.Name) doesnt exist and a location is also not provided to create one."
                            }

                            if (!$resourceGroupServicePublish) {
                                $resourceGroupServicePublish = $vnetObjectArray.service.publish.resourceGroup | Where-Object { $_ -notlike '' }
                            }
                            if (!$resourceGroupServicePublish) {
                                $resourceGroupServicePublish = $routeTableObjectArray.service.publish.resourceGroup | Where-Object { $_ -notlike '' }
                            }
                            if (!$resourceGroupServicePublish) {
                                $resourceGroupServicePublish = $nsgObjectArray.service.publish.resourceGroup | Where-Object { $_ -notlike '' }
                            }

                            if (!$resourceGroupServicePublish) {
                                Write-Error "Resource Group $($ResourceGroup.Name) doesn't exist and need to be created. Please provide Resource Group Service to Publish."
                            }

                            if ($resourceGroupServicePublish -is [array]) {
                                [String]$resourceGroupServicePublishString = $resourceGroupServicePublish[0]
                            }
                            else {
                                [String]$resourceGroupServicePublishString = $resourceGroupServicePublish
                            }

                            $createRG = $true

                            $service = @{
                                publish = @{
                                    resourceGroup = $resourceGroupServicePublishString
                                }
                            }
                        }
                        else {
                            $RGlocation = $ifResourceGroupExists.location
                            $createRG = $false
                            $service = @{
                                publish = @{
                                    resourceGroup = "none"
                                }
                            }
                        }

                        # Adding Objects to resourceGroup Object
                        Write-Verbose "Adding '$($ResourceGroup.Name)' to Resource Group Object List"
                        $ResourceGroupObject = @{

                            resourceGroup         = @{
                                name     = $ResourceGroup.Name;
                                location = $RGlocation;
                                service  = $service;
                                createRG = $createRG;
                            };

                            vnets                 = $vnetObjectArray;
                            routeTables           = $routeTableObjectArray;
                            networkSecurityGroups = $nsgObjectArray
                        }

                        $resourceGroupObjectArray.Add($ResourceGroupObject) > $Null
                        Write-Verbose "'$($ResourceGroup.Name)' Added"
                    }
                }
            }

            # Code to Create Object from Yml
            if ($SettingsObject) {

                function createNetworkObjectFromYml {
                    param (
                        [string]
                        $YmlFilePath,
                        [string]
                        $ObjectType,
                        [Boolean]
                        $hasGroups
                    )

                    if ($_.contains('/') -or $_.contains('\')) {
                        $interimPath = Resolve-FilePath -NestedFile $YmlFilePath
                    }
                    else {
                        $interimPath = "$(Split-Path $SettingsFile)/$ObjectType/$YmlFilePath"
                    }

                    if (!$interimPath.contains('.yml')){
                        $interimPath = "$interimPath.yml"
                    }

                    try {
                        $returnObject = Get-CmAzSettingsFile -Path $interimPath
                    }
                    catch {

                        try {
                            $interimPath.replace('/', '\')
                            $returnObject = Get-CmAzSettingsFile -Path $interimPath
                        }
                        catch {
                            throw "Not able to find file $YmlFilePath"
                        }

                    }

                    if ($hasGroups) {

                        ForEach ($object in $returnObject) {

                            if ($ObjectType -eq "networkSecurityGroups") {
                                $groupName = "ruleGroup"
                                $childObject = "rules"
                            }
                            elseif ($ObjectType -eq "routeTables") {
                                $groupName = "routeGroup"
                                $childObject = "routes"
                            }

                            if ($object.$groupName) {

                                $object.$groupName | ForEach-Object {

                                    try {
                                        $interimGroupPath = "$(Split-Path $interimPath)/groups/$_"

                                        if (!$interimGroupPath.contains('.yml')){
                                            $interimGroupPath = "$interimGroupPath.yml"
                                        }

                                        $returnObjectGroup = Get-CmAzSettingsFile -Path $interimGroupPath
                                    }
                                    catch [System.Management.Automation.RuntimeException] {

                                        try {
                                            $interimGroupPath.replace('/', '\')
                                            $returnObjectGroup = Get-CmAzSettingsFile -Path $interimGroupPath
                                        }
                                        catch {
                                            throw "Not able to find file at $interimGroupPath."
                                        }

                                    }

                                    $object.$childObject += $returnObjectGroup
                                }

                                $object.remove($groupName)
                            }
                        }
                    }

                    return $returnObject
                }

                $SettingsObject.networking | ForEach-Object {
                    $vnetObjectArray = [System.Collections.ArrayList]@()
                    $routeTableObjectArray = [System.Collections.ArrayList]@()
                    $nsgObjectArray = [System.Collections.ArrayList]@()

                    $ResourceGroup = $_.ResourceGroupName

                    $GlobalServiceContainer = $_

                    # Set Vnet object
                    if ($_.vnets) {

                        Write-Verbose "Importing virtual networks ..."

                        $_.vnets | ForEach-Object {

                            $vnetObjectYml = createNetworkObjectFromYml -YmlFilePath $_ -ObjectType "vnets" -hasGroups $false

                            if (!$vnetObjectYml.subnets) {
                                Write-Error "$($vnetObjectYml.vnetName) is missing subnet configuration."
                            }

                            $vnetObjectYml.subnets | Where-Object { !$_.networkSecurityGroup } | ForEach-Object {
                                $_.networkSecurityGroup = ""
                            }

                            $vnetObjectYml.subnets | Where-Object { !$_.routeTable } | ForEach-Object {
                                $_.routeTable = ""
                            }

                            $vnetObjectYml | Where-Object { !$_.location } | ForEach-Object {
                                $_.location = ""
                            }

                            $vnetObjectYml | Where-Object { !$_.dnsServers } | ForEach-Object {
                                $_.dnsServers = ""
                            }

                            $vnetObjectYml | Where-Object { !$_.virtualNetworkPeers } | ForEach-Object {
                                $_.virtualNetworkPeers = @()
                            }

                            $vnetObjectYml | ForEach-Object {
                                Set-GlobalServiceValues -GlobalServiceContainer $GlobalServiceContainer  -ServiceKey "vnet" -ResourceServiceContainer $_
                            }

                            $vnetObjectArray += $vnetObjectYml
                            Write-Verbose "Vnets from $_ added to '$ResourceGroup'"
                        }
                    }

                    # Set Route Table Object
                    if ($_.routeTables) {

                        Write-Verbose "Importing route tables..."

                        $_.routeTables | ForEach-Object {

                            $routeTableObjectYml = createNetworkObjectFromYml -YmlFilePath $_ -ObjectType "routeTables" -hasGroups $true

                            $routeTableObjectYml | Where-Object { !$_.location } | ForEach-Object {
                                $_.location = ""
                            }

                            $routeTableObjectYml | Where-Object { !$_.routePropagation } | ForEach-Object {
                                $_.routePropagation = $false
                            }

                            $routeTableObjectYml | ForEach-Object {
                                Set-GlobalServiceValues -GlobalServiceContainer $GlobalServiceContainer -ServiceKey "routeTable" -ResourceServiceContainer $_
                            }

                            $routeTableObjectArray += $routeTableObjectYml
                            Write-Verbose "Route tables from $_ added to '$ResourceGroup'"
                        }
                    }

                    # Set network Security group Object
                    if ($_.networkSecurityGroups) {

                        Write-Verbose "Importing network security groups..."

                        $_.networkSecurityGroups | ForEach-Object {

                            $nsgObjectYml = createNetworkObjectFromYml -YmlFilePath $_ -ObjectType "networkSecurityGroups" -hasGroups $true

                            $nsgObjectYml | Where-Object { !$_.location } | ForEach-Object {
                                $_.location = ""
                            }

                            $nsgObjectYml | ForEach-Object {
                                Set-GlobalServiceValues -GlobalServiceContainer $GlobalServiceContainer  -ServiceKey "networkSecurityGroup" -ResourceServiceContainer $_
                            }

                            $nsgObjectArray += $nsgObjectYml
                            Write-Verbose "Network Security Groups from $_ added to '$ResourceGroup'"
                        }
                    }

                    # Default values if required for ARM template sanity checks
                    if (!$vnetObjectArray) {
                        $vnetObjectArray = @(@{vnetName = "none"; vnetPeerings = @(); location = ""; dnsServers = ""; addressSpace = @("10.10.0.0/24"); subnets = @(@{subnetName = "none"; cidr = "0.0.0.0/0" }); service = @{publish = @{vnet = "" } } })
                    }

                    if (!$routeTableObjectArray) {
                        $routeTableObjectArray = @(@{tableName = "none"; location = ""; routes = @(@{routeName = "none"; cidr = "0.0.0.0/0"; nextHopType = "VirtualAppliance"; nextHopIpAddress = "10.10.10.10" }); service = @{publish = @{routeTable = "" } } })
                    }

                    if (!$nsgObjectArray) {
                        $nsgObjectArray = @(@{nsgName = "none"; location = ""; rules = @(@{ruleName = "none"; description = "none"; priority = "none"; direction = "none"; sourceIp = "10.10.10.10"; sourcePort = 3389; destinationIp = "10.10.10.11"; destinationPort = 3389; protocol = "Tcp"; Access = "allow" }); service = @{publish = @{networkSecurityGroup = "" } } })
                    }

                    # Build Resource Group Config
                    $ifResourceGroupExists = Get-AzResourceGroup -Name $_.resourceGroupName -ErrorAction SilentlyContinue

                    if (!$ifResourceGroupExists) {
                        $RGlocation = $_.location

                        if (!$RGlocation -and $vnetObjectArray[0].location ) {
                            $RGlocation = $vnetObjectArray[0].location
                        }

                        if (!$RGlocation) {
                            Write-Error "Resource Group $($_.resourceGroupName) doesnt exist and a location is also not provided to create one."
                        }

                        if (!$_.service.publish.resourceGroup) {
                            Write-Error "Resource Group doesn't exist and need to be created. Please provide Resource Group Service to Publish."
                        }

                        $createRG = $true

                        $service = @{
                            publish = @{
                                resourceGroup = $_.service.publish.resourceGroup
                            }
                        }
                    }
                    else {
                        $RGlocation = $ifResourceGroupExists.location
                        $createRG = $false
                        $service = @{
                            publish = @{
                                resourceGroup = ""
                            }
                        }
                    }

                    # Adding Objects to resourceGroup Object
                    Write-Verbose "Adding '$ResourceGroup' to Resource Group Object List"
                    $ResourceGroupObject = @{

                        resourceGroup         = @{
                            name     = $ResourceGroup;
                            location = $RGlocation;
                            createRG = $createRG;
                            service  = $service
                        };

                        vnets                 = $vnetObjectArray;
                        routeTables           = $routeTableObjectArray;
                        networkSecurityGroups = $nsgObjectArray
                    }

                    $resourceGroupObjectArray.Add($ResourceGroupObject) > $Null
                    Write-Verbose "'$ResourceGroup' Added"
                }
            }

            # Arm Deployment
            Write-Verbose "Deploying resource groups..."
            New-AzDeployment `
                -Name "Cm_network_resource_group_deployment_$(Get-Date -Format 'ddMMyyyyHHmmsssss')" `
                -TemplateFile $PSScriptRoot\New-CmAzIaasNetworking.ResourceGroups.json `
                -Location $resourceGroupObjectArray[0].resourceGroup.location `
                -NetworkingArrayObject $resourceGroupObjectArray

            if ($resourceGroupObjectArray.networkSecurityGroups.nsgName[0] -ne 'none' -and $resourceGroupObjectArray.networkSecurityGroups.nsgName -ne 'none') {

                Write-Verbose "Nsgs found..."

                $storageServiceDependency = $SettingsObject.service.dependencies.storage
                $workspaceServiceDependency = $SettingsObject.service.dependencies.workspace

                if (!$storageServiceDependency) {

                    if ($resourceGroupObjectArray.networkSecurityGroups.service.dependencies.storage -is [array]) {
                        $storageServiceDependency = $resourceGroupObjectArray.networkSecurityGroups.service.dependencies.storage[0]
                    }
                    else {
                        $storageServiceDependency = $resourceGroupObjectArray.networkSecurityGroups.service.dependencies.storage
                    }
                }

                if (!$workspaceServiceDependency) {

                    if ($resourceGroupObjectArray.networkSecurityGroups.service.dependencies.workspace -is [array]) {
                        $workspaceServiceDependency = $resourceGroupObjectArray.networkSecurityGroups.service.dependencies.workspace[0]
                    }
                    else {
                        $workspaceServiceDependency = $resourceGroupObjectArray.networkSecurityGroups.service.dependencies.workspace
                    }
                }

                if (!$storageServiceDependency) {
                    Write-Error "Please provide a storage service value." -Category InvalidArgument
                }

                if (!$workspaceServiceDependency) {
                    Write-Error "Please provide a workspace service value." -Category InvalidArgument
                }

                $storageAccounts = Get-CmAzService -Service $storageServiceDependency -ThrowIfUnavailable
                $workspace = Get-CmAzService -Service $workspaceServiceDependency -ThrowIfUnavailable -ThrowIfMultiple

                foreach ($resourceGroupObject in $resourceGroupObjectArray) {

                    foreach ($nsg in $resourceGroupObject.networkSecurityGroups) {

                        $nsg.resourceGroup = $resourceGroupObject.resourceGroup

                        if (!$nsg.location) {
                            $nsg.location = $resourceGroupObject.resourceGroup.location
                        }

                        $filteredStorageAccounts = $storageAccounts | Where-Object { $_.location -eq $nsg.location }

                        if ($filteredStorageAccounts -Is [array]) {
                            $filteredStorageAccounts = $filteredStorageAccounts[0]
                        }

                        $nsg.storageAccountId = $filteredStorageAccounts.id
                    }
                }

                $networkWatcherResourceGroupName = "NetworkWatcherRG"

                New-AzResourceGroup -Location $workspace.location -Name $networkWatcherResourceGroupName -Force

                Write-Verbose "Deploying nsgs..."
                New-AzDeployment `
                    -TemplateFile $PSScriptRoot\New-CmAzIaasNetworking.Nsgs.json `
                    -Location $workspace.location `
                    -Locations ($resourceGroupObjectArray.networkSecurityGroups.location | Sort-Object | Get-Unique) `
                    -NetworkWatcherResourceGroupName $networkWatcherResourceGroupName `
                    -Nsgs $resourceGroupObjectArray.networkSecurityGroups `
                    -Workspace $workspace
            }

            Write-Verbose "Deploying vnets and udrs..."
            New-AzDeployment `
                -TemplateFile $PSScriptRoot\New-CmAzIaasNetworking.json `
                -Location $resourceGroupObjectArray[0].resourceGroup.location `
                -NetworkingArrayObject $resourceGroupObjectArray

            if ($resourceGroupObjectArray.vnets.virtualnetworkpeers) {
                $filteredVnetObject = $resourceGroupObjectArray.vnets | Where-Object { $_.virtualnetworkpeers }
                $vnetPeeringsObjectArray = [System.Collections.ArrayList]@()

                foreach ($Vnet in $filteredVnetObject) {

                    foreach ($peeringVnet in $Vnet.virtualnetworkpeers) {

                        $currentVnetObject = $resourceGroupObjectArray | Where-Object { $_.vnets.vnetname -eq $Vnet.vnetname }
                        $peeringVnetObject = $resourceGroupObjectArray | Where-Object { $_.vnets.vnetname -eq $peeringVnet }

                        $vnetPeeringsObject = @{

                            sourceVnetRg           = $currentVnetObject.resourceGroup.name
                            sourceVnetName         = $Vnet.vnetname
                            TargetVnetName         = $peeringVnet
                            TargetVnetRg           = $peeringVnetObject.resourceGroup.name
                            TargetVnetAddressSpace = $peeringVnetObject.vnets.addressSpace
                        }

                        $vnetPeeringsObjectArray.Add($vnetPeeringsObject) > $Null
                    }
                }

                Write-Verbose "Configuring vnet peerings..."
                New-AzDeployment `
                    -TemplateFile $PSScriptRoot\New-CmAzIaasNetworking.vnetPeerings.json `
                    -Location $resourceGroupObjectArray[0].resourceGroup.location `
                    -VnetPeeringsObjectArray $vnetPeeringsObjectArray
            }

            $resourceGroupsToSet = @()
            $resourceGroupsToSet += ($resourceGroupObjectArray.resourceGroup | Where-object -Property createRG -eq $true).name
            $resourceGroupsToSet += $networkWatcherResourceGroupName

            if ($resourceGroupsToSet) {
                Set-DeployedResourceTags -TagSettingsFile $TagSettingsFile -ResourceGroupIds $resourceGroupsToSet
            }

            [System.Collections.ArrayList]$resourcesToSet = @()

            $resourcesToSet += $resourceGroupObjectArray.vnets.vnetName | Where-Object { $_ -ne "none" }
            $resourcesToSet += $resourceGroupObjectArray.networkSecurityGroups.nsgName | Where-Object { $_ -ne "none" }
            $resourcesToSet += $resourceGroupObjectArray.routeTables.tableName | Where-Object { $_ -ne "none" }

            Set-DeployedResourceTags -TagSettingsFile $TagSettingsFile -ResourceIds $resourcesToSet

            Write-Verbose "Finished."
        }
    }
    catch {
        $PSCmdlet.ThrowTerminatingError($PSItem)
    }
}