Framework/Core/SVT/Services/ERvNet.ps1
#using namespace Microsoft.Azure.Commands.ExpressRouteVirtualNetwork.Models Set-StrictMode -Version Latest class ERvNet : SVTIaasBase { ERvNet([string] $subscriptionId, [SVTResource] $svtResource): Base($subscriptionId, $svtResource) { } hidden [ControlResult] CheckPublicIps([ControlResult] $controlResult) { if($null -ne $this.vNetNicsOutput ) { $controlResult.AddMessage([MessageData]::new("Analyzing all the NICs configured in the VNet")); $publicIpCount = (($this.vNetNicsOutput | Where-Object {!([System.String]::IsNullOrWhiteSpace($_.PublicIpAddress))}) | Measure-Object).count if($publicIpCount -gt 0) { $publicIPList = @() $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Below Public IP(s) on the ERVnet")); $this.vNetNicsOutput | ForEach-Object{ Set-Variable -Name nic -Scope Local -Value $_ $publicIP = $nic | Select-Object NICName, VMName, PrimaryStatus, NetworkSecurityGroupName, PublicIpAddress, PrivateIpAddress $publicIPList += $publicIP $controlResult.AddMessage([MessageData]::new($publicIP)); } $controlResult.SetStateData("Public IP(s) on the ERVnet", $publicIPList); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No Public IP is configured in any NIC on the ERVnet")); } } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No NICs found on the ERVNet")); } if(($this.vNetNicsWIssues | Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Not able to validate following NICs:", $this.vNetNicsWIssues)); } if(($this.vNetPIPIssues | Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Not able to validate following IPConfigurations:", $this.vNetPIPIssues)); } return $controlResult; } hidden [ControlResult] CheckIPForwardingforNICs([ControlResult] $controlResult) { if($null -ne $this.vNetNicsOutput) { [array] $vNetNicsIPFwed = $this.vNetNicsOutput | Where-Object { $_.EnableIPForwarding } if($null -ne $vNetNicsIPFwed -and ($vNetNicsIPFwed | Measure-Object).count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("IP Forwarding is enabled for below NIC(s) in ERVNet")); $controlResult.AddMessage([MessageData]::new(($vNetNicsIPFwed | Select-Object NICName, EnableIPForwarding))); $controlResult.SetStateData("IP Forwarding is enabled for NIC(s) in ERVNet", $vNetNicsIPFwed); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("There are no NICs with EnableIPForwarding turned on the ERVNet")); } } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No NICs found on the ERVNet")); } if(($this.vNetNicsWIssues | Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Not able to validate following NICs:", $this.vNetNicsWIssues)); } if(($this.vNetPIPIssues | Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Not able to validate following IPConfigurations:", $this.vNetPIPIssues)); } return $controlResult; } hidden [ControlResult] CheckNSGUseonGatewaySubnet([ControlResult] $controlResult) { $gateWaySubnet = $this.ResourceObject.Subnets | Where-Object { $_.Name -eq "GatewaySubnet" } if($null -ne $gateWaySubnet) { if($null -ne $gateWaySubnet.NetworkSecurityGroup -and -not [System.String]::IsNullOrWhiteSpace($gateWaySubnet.NetworkSecurityGroup.Id)) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("NSG is configured on the Gateway Subnet of ERVNet", ($gateWaySubnet | Select-Object Name, NetworkSecurityGroupText))); $controlResult.SetStateData("Gateway subnet of ERVNet", ($gateWaySubnet | Select-Object Name, NetworkSecurityGroup)); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("There are no NSG's configured on the Gateway subnet of ERVNet")); } } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No Gateway subnet found on the ERVNet")); } return $controlResult; } hidden [ControlResult] CheckVnetPeering([ControlResult] $controlResult) { $vnetPeerings = Get-AzVirtualNetworkPeering -VirtualNetworkName $this.ResourceContext.ResourceName -ResourceGroupName $this.ResourceContext.ResourceGroupName if($null -ne $vnetPeerings -and ($vnetPeerings|Measure-Object).count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Below peering found on ERVNet", $vnetPeerings)); $controlResult.SetStateData("Peering found on ERVNet", $vnetPeerings); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No VNet peerings found on ERVNet", $vnetPeerings)); } return $controlResult; } hidden [ControlResult] CheckMultiNICVMUsed([ControlResult] $controlResult) { $VMNics = @() if($null -ne $this.vNetNicsOutput) { $vNetNicsMultiVM = $this.vNetNicsOutput | Group-Object VMId | Where-Object {-not [System.String]::IsNullOrWhiteSpace($_.Name) -and $_.Count -gt 1} $hasTCPPassed = $true if($null -ne $vNetNicsMultiVM) { $vNetNicsMultiVM | ForEach-Object{ $NICGroup = @() $NICGroup += $_.Group if($null -ne $NICGroup) { $NICGroup | ForEach-Object{ Set-Variable -Name tempNIC -Value $_ if($null -ne $tempNIC.IpConfigurations ) { $tempIpConfigurations = [array]($tempNIC.IpConfigurations) $tempIpConfigurations | ForEach-Object{ Set-Variable -Name tempIPConfig -Value $_ if($null -ne $tempIPConfig.properties.Subnet) { if(-not $tempIPConfig.properties.Subnet.Id.StartsWith($this.ResourceObject.Id,"CurrentCultureIgnoreCase")) { $hasTCPPassed = $false } } } } } $VMNics += $NICGroup } } } $controlResult.AddMessage([MessageData]::new(($this.vNetNicsOutput | Group-Object VMId | Where-Object {-not [System.String]::IsNullOrWhiteSpace($_.Name) } | Select-Object @{Name="[Count of NICs]";Expression= {$_.Count}}, @{Name="[VM ResourceID]";Expression= {$_.Name}}))); if(-not $hasTCPPassed) { $controlResult.SetStateData("VM NIC details", $VMNics); $controlResult.VerificationResult = [VerificationResult]::Failed; } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("There are no VMs with more than one NIC")); } } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No NICs found on the ERVNet")); } if(($this.vNetNicsWIssues | Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Not able to validate following NICs:", $this.vNetNicsWIssues)); } if(($this.vNetPIPIssues | Measure-Object).Count -gt 0) { $controlResult.AddMessage([MessageData]::new("Not able to validate following IPConfigurations:", $this.vNetPIPIssues)); } return $controlResult; } hidden [ControlResult] CheckUDRAddedOnSubnet([ControlResult] $controlResult) { $subnetsWithUDRs = $this.ResourceObject.Subnets | Where-Object {$null -ne $_.RouteTable -and -not [System.String]::IsNullOrWhiteSpace($_.RouteTable.Id)} if($null -ne $subnetsWithUDRs -and ($subnetsWithUDRs | Measure-Object).count -gt 0) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new(($subnetsWithUDRs | Select-Object Name, RouteTableText))); $controlResult.SetStateData("UDRs found on any Subnet of ERVNet", $subnetsWithUDRs); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No UDRs found on any Subnet of ERVNet")); } return $controlResult; } hidden [ControlResult] CheckGatewayUsed([ControlResult] $controlResult) { $nonERvNetGateways = @() $hasTCPPassed = $true $gateways = Get-AzVirtualNetworkGateway -ResourceGroupName $this.ResourceContext.ResourceGroupName $count = 0 if(($null -ne $gateways) -and (($gateways | Measure-Object).count -gt 0)) { $gateways | ForEach-Object{ Set-Variable -Name gateway -Scope Local -Value $_ if($null -ne $gateway.IpConfigurations) { $tempIpConfigurations = [array]($gateway.IpConfigurations) $tempIpConfigurations | ForEach-Object{ Set-Variable -Name tempIpConfig -Value $_ if($tempIpConfig.Subnet.Id.StartsWith($this.ResourceObject.Id,"CurrentCultureIgnoreCase")) { if($gateway.GatewayType -ne "ExpressRoute") { $nonERvNetGateway = New-Object System.Object $nonERvNetGateway | Add-Member -type NoteProperty -name ResourceName -Value $gateway.Name $nonERvNetGateway | Add-Member -type NoteProperty -name ResourceGroupName -Value $gateway.ResourceGroupName $nonERvNetGateway | Add-Member -type NoteProperty -name GatewayType -Value $gateway.GatewayType $nonERvNetGateway | Add-Member -type NoteProperty -name VPNType -Value $gateway.VpnType $nonERvNetGateways += $nonERvNetGateway $hasTCPPassed = $false } $controlResult.AddMessage([MessageData]::new("GateWay Name: " + $gateway.Name + " GatewayType: " + $gateway.GatewayType)); $count++ } } } } } if($count -eq 0) { $controlResult.AddMessage([MessageData]::new("No gateways found")); } if(-not $hasTCPPassed) { $controlResult.SetStateData("Non Express Route gateways in ERVNet", $nonERvNetGateways); $controlResult.VerificationResult = [VerificationResult]::Failed; } else { $controlResult.VerificationResult = [VerificationResult]::Passed; } return $controlResult; } hidden [ControlResult] CheckInternalLoadBalancers([ControlResult] $controlResult) { $invalidlbList = @() $hasTCPPassed = $true $ilbs = Get-AzLoadBalancer $count = 0 if($null -ne $ilbs -and ($ilbs|Measure-Object).count -gt 0) { $ilbs | ForEach-Object { Set-Variable -Name ilb -Value $_ -Scope Local if($null -ne $ilb -and $null -ne $ilb.FrontendIpConfigurations) { $ilb.FrontendIpConfigurations |ForEach-Object{ Set-Variable -Name frontEndIpConfig -Scope Local -Value $_ if($null -ne $frontEndIpConfig.Subnet) { if($frontEndIpConfig.Subnet.Id.StartsWith($this.ResourceObject.Id,"CurrentCultureIgnoreCase")) { if($null -ne $frontEndIpConfig.PublicIpAddress) { $subParts = $frontEndIpConfig.PublicIpAddress.Id.Split('/') $publicIpResourceName = $subParts[$subParts.Length-1] $pubResourceName = Get-AzPublicIpAddress -Name $publicIpResourceName -ResourceGroupName $this.ResourceContext.ResourceGroupName $hasTCPPassed = $false $invalidlb = New-Object System.Object $invalidlb | Add-Member -type NoteProperty -name Name -Value $ilbs.Name $invalidlb | Add-Member -type NoteProperty -name IpAddress -Value $pubResourceName.IpAddress $invalidlbList += $invalidlb $controlResult.AddMessage([MessageData]::new("ILB Name: " + $ilbs.Name + " PublicIP: " + $pubResourceName.IpAddress)); } $controlResult.AddMessage([MessageData]::new("No public Ips found on ILB: " + $ilbs.Name)); $count++ } } } } } } if($count -eq 0) { $controlResult.AddMessage([MessageData]::new("No ILB found")); } if(-not $hasTCPPassed) { $controlResult.SetStateData("Non internal LBs in ERVNet", $invalidlbList); $controlResult.VerificationResult = [VerificationResult]::Failed; } else { $controlResult.VerificationResult = [VerificationResult]::Passed; } return $controlResult; } hidden [ControlResult] CheckOnlyNetworkResourceExist([ControlResult] $controlResult) { $resources = [array](Get-AzResource -ResourceGroupName $this.ResourceContext.ResourceGroupName) if($null -ne $resources) { $nonApprovedResources = [array]($resources | Where-Object { -not $_.ResourceType.StartsWith("Microsoft.Network","CurrentCultureIgnoreCase")}) if($null -ne $nonApprovedResources ) { $controlResult.SetStateData("Non approved resources in ERVNet ResourceGroup", $nonApprovedResources); $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Other resource types found apart from Microsoft.Network\*. Below are the Resource IDs and Resource Types available under the ResourceGroup - ["+ $this.ResourceContext.ResourceGroupName +"]",($nonApprovedResources | Select-Object ResourceType, ResourceID))); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No other resource types found apart from Microsoft.Network\* . Below are the Resource ID available under the ResourceGroup - ["+ $this.ResourceContext.ResourceGroupName +"]")); } $controlResult.AddMessage([MessageData]::new("Resources configured under ResourceGroup - ["+ $this.ResourceContext.ResourceGroupName +"]",($resources | Select-Object ResourceType, ResourceID))); } else { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("No other resources found under the ResourceGroup - ["+ $this.ResourceContext.ResourceGroupName +"]")); } return $controlResult; } hidden [ControlResult] CheckResourceLockConfigured([ControlResult] $controlResult) { $locks = [array](Get-AzResourceLock -ResourceGroupName $this.ResourceContext.ResourceGroupName -AtScope) if($null -eq $locks -or $locks.Length -le 0) { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("No resource locks are configured at the ResourceGroup scope for - ["+ $this.ResourceContext.ResourceName +"]")); } else { if(($locks | Where-Object {$_.Properties.Level -eq $this.ControlSettings.ERvNet.ResourceLockLevel } | Measure-Object).Count -gt 0) { $controlResult.AddMessage([VerificationResult]::Passed, [MessageData]::new("Found resource locks configured at the ResourceGroup scope for - ["+ $this.ResourceContext.ResourceName +"]", $locks)); } else { $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("No *$($this.ControlSettings.ERvNet.ResourceLockLevel)* resource locks are configured at the ResourceGroup scope for - ["+ $this.ResourceContext.ResourceName +"]")); } } return $controlResult; } hidden [ControlResult] CheckARMPolicyConfigured([ControlResult] $controlResult) { $controlSettings = $this.LoadServerConfigFile("Subscription.ARMPolicies.json"); $output = @() $missingPolicies = @() if($null -ne $controlSettings -and [Helpers]::CheckMember($controlSettings,"Policies")) { $policies = $controlSettings.Policies $enabledPolicies = @() $sdoPolicies = @() #Filter to get only enabled and sdo tagged policy $enabledPolicies += $policies | Where-Object {( ($_.tags.Trim().ToLower().Contains("sdo")) -and ($_.enabled) )} #Filter to get policy applicable for current ErvNet RG if(($enabledPolicies | Measure-Object).Count -gt 0){ $enabledPolicies | ForEach-Object { $ErvNetRGPatterns = ((($_.applicableForRGs | ForEach-Object {'^' + [regex]::escape($_) + '$' }) -join '|') ) -replace '[\\]','' if(($this.ResourceContext.ResourceGroupName.ToLower() -imatch $ErvNetRGPatterns)){ $sdoPolicies += $_ } } } if(($sdoPolicies | Measure-Object).Count -gt 0) { $sdoPolicies | ForEach-Object{ Set-Variable -Name pol -Scope Local -Value $_ Set-Variable -Name policyDefinitionName -Scope Local -Value $_.policyDefinitionName Set-Variable -Name tags -Scope Local -Value $_.tags $foundPolicies = [array](Get-AzPolicyAssignment | Where-Object {$_.Name -eq $policyDefinitionName}) if($null -ne $foundPolicies) { if($foundPolicies.Length -gt 0) { $output += $pol } else{ $missingPolicies += $pol } } else{ $missingPolicies += $pol } } } else { $controlResult.AddMessage([VerificationResult]::Passed,[MessageData]::new("No mandatory ARM policies required to be configured on the subscription because of ERNetwork.")); } } if(($missingPolicies | Measure-Object).Count -le 0) { $controlResult.VerificationResult = [VerificationResult]::Passed; } else { $missingPolicies = $missingPolicies | select-object "policyDefinitionName" $controlResult.SetStateData("Missing mandatory policies", $missingPolicies); $controlResult.AddMessage([VerificationResult]::Failed, [MessageData]::new("Following mandatory policies are missing which are demanded by the control tags:",$missingPolicies)); } return $controlResult; } } |