From e4d74f6f69c2f861abbfffb48592a3c061ef9948 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Tue, 9 Aug 2022 18:33:04 +0530 Subject: [PATCH 01/65] IP Filtering changes with EP script --- .../Invoke-ConfigureMitigation.ps1 | 301 ++++++++++++++++++ .../Invoke-RollbackIPFiltering.ps1 | 140 ++++++++ .../Invoke-ValidateMitigation.ps1 | 238 ++++++++++++++ .../DataCollection/Get-ExchangeServerIPs.ps1 | 77 +++++ .../Get-IPFilteringPrerequisitesCheck.ps1 | 35 ++ .../Get-IPRangeAllowListFromFile.ps1 | 108 +++++++ .../ExchangeExtendedProtectionManagement.ps1 | 224 +++++++++++-- 7 files changed, 1093 insertions(+), 30 deletions(-) create mode 100644 Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 create mode 100644 Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 create mode 100644 Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 create mode 100644 Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 create mode 100644 Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 create mode 100644 Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 new file mode 100644 index 0000000000..da41172594 --- /dev/null +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -0,0 +1,301 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +. $PSScriptRoot\..\..\..\..\Shared\Invoke-ScriptBlockHandler.ps1 +. $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 + +# Steps: TurnOffEP -> Install windows feature -> GetLocalIP -> backup configuration -> CreateAllowRules -> SetDefaultToDeny +$ConfigureMitigation = { + param( + [Object]$Arguments + ) + + $SiteVDirLocation = $Arguments.SiteVDirLocation + $IpRangesForFiltering = $Arguments.IpRangesForFiltering + + $results = @{ + IsTurnOffEPSuccessful = $false + IsWindowsFeatureInstalled = $false + IsGetLocalIPSuccessful = $false + IsBackUpSuccessful = $false + IsCreateIPRulesSuccessful = $false + IsSetDefaultRuleSuccessful = $false + ErrorContext = $null + IPsNotAdded = @() + LocalIPs = @() + } + + function Backup-currentIpFilteringRules { + param( + $BackupPath + ) + + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + + return $true + } + + function Get-LocalIpAddresses { + $ips = @() + $interfaces = Get-NetIPAddress + foreach ($interface in $interfaces) { + if ($interface.AddressState -eq 'Preferred') { + $ips += $interface.IPAddress + } + } + + return $ips + } + + # Set EP to None + function TurnOffEP { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation + ) + + $Filter = 'system.webServer/security/authentication/windowsAuthentication/extendedProtection' + $IISPath = 'IIS:\' + + $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking + + if ($ExtendedProtection -ne "None") { + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" + } + } + + # Create ip allow list from user provided ip subnets + function CreateIPRangeAllowList { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $true)] + [object[]]$IpFilteringRules, + [Parameter(Mandatory = $true)] + [hashtable] $results + ) + + $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/','-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + + Backup-currentIpFilteringRules -BackupPath $backupPath + $results.IsBackUpSuccessful = $true + + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + + $RulesToBeAdded = @() + $IPsNotAdded = @() + + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + + foreach ($IpFilteringRule in $IpFilteringRules) { + $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } + + if ($null -eq $ExistingIPSubnetRule) { + if ($IpFilteringRule.Type -eq "Single IP") { + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; } + } else { + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; } + } + } else { + if ($IpFilteringRule.Type -eq "Single IP") { + $IpString = $IpFilteringRule.IP + # Write-Verbose ("Unable to add allow rule for ip address: {0} as an already existing rule exist for this." -f $IpFilteringRule.IP ) + } else { + $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + # Write-Verbose ("Unable to add allow rule for ip subnet: {0}/{1} as an already existing rule exist for this." -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask ) + } + + $IPsNotAdded += $IpString + } + } + + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop + + $results.IsCreateIPRulesSuccessful = $true + + # Setting default to deny + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $false + $results.IsSetDefaultRuleSuccessful = $true + return $IPsNotAdded + } + + try { + TurnOffEP -SiteVDirLocation $SiteVDirLocation + $results.IsTurnOffEPSuccessful = $true + + if ($null -ne $IpRangesForFiltering) { + $installResult = Install-WindowsFeature Web-IP-Security + if (-not $installResult) { + throw ("Unable to install Windows feature Web-IP-Security which is required to add IP filtering rules to IIS.") + } + + $results.IsWindowsFeatureInstalled = $true + + $localIPs = Get-LocalIpAddresses + $results.IsGetLocalIPSuccessful = $true + + $localIPs | ForEach-Object { + $IpRangesForFiltering += @{Type="Single IP"; IP=$_; SubnetMask=$null; Allowed=$true } + } + + $results.LocalIPs = $localIPs + $results.IPsNotAdded = CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results + } + } catch { + $results.ErrorContext = $_ + } + + return $results +} + +function GetCommaSaperatedString { + param( + [Parameter(Mandatory = $true)] + [object[]]$list + ) + + $string = "" + foreach ($element in $list) { + $string += ($element.ToString() + ", ") + } + + return $string.Trim(", ") +} + +function Invoke-ConfigureMitigation { + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string[]]$ExchangeServers, + [Parameter(Mandatory = $false)] + [object[]]$ipRangeAllowListRules, + [Parameter(Mandatory = $true)] + [string]$Site, + [Parameter(Mandatory = $true)] + [string]$VDir + ) + + begin { + $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' + $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' + + $progressParams = @{ + Activity = "Turning Off EP and applying IP filtering Rules" + Status = [string]::Empty + PercentComplete = 0 + } + + $ShouldConfigureFilter = ($null -ne $ipRangeAllowListRules) + + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + } process { + $SiteVDirLocation = $Site + if ($VDir -ne '') { + $SiteVDirLocation += '/' + $VDir + } + + $scriptblockArgs = [PSCustomObject]@{ + SiteVDirLocation = $SiteVDirLocation + IpRangesForFiltering = $ipRangeAllowListRules + } + + $counter = 0 + $totalCount = $ExchangeServers.Count + foreach ($Server in $ExchangeServers) { + $baseStatus = "Processing: $Server -" + $progressParams.PercentComplete = ($counter / $totalCount * 100) + $progressParams.Status = "$baseStatus Applying rules" + Write-Progress @progressParams + $counter ++; + + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, [string]::Join(", ", $ipRangeAllowListRules)) + Write-Host ("Applying Mitigations on Server {0}" -f $Server) + $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs + + if ($resultsInvoke.IsTurnOffEPSuccessful) { + Write-Host ("Successfully turned Off Extended Protection") -ForegroundColor Green + } else { + Write-Host ("Script failed to Turn Off Extended protection with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $FailedServersEP += $Server + $FailedServersFilter += $Server + continue + } + + if (-not $ShouldConfigureFilter) { + continue + } + + if ($resultsInvoke.IsWindowsFeatureInstalled) { + Write-Host ("Successfully installed windows feature - Web-IP-Security") -ForegroundColor Green + } else { + Write-Host ("Script failed to install windows feature - Web-IP-Security with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $FailedServersFilter += $Server + continue + } + + if ($resultsInvoke.IsGetLocalIPSuccessful) { + Write-Host ("Successfully retrieved local IPs for the server") -ForegroundColor Green + if ($null -ne $resultsInvoke.LocalIPs -and $resultsInvoke.LocalIPs.Length -gt 0) { + Write-Verbose ("Local IPs detected for this server: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.LocalIPs)) + } else { + Write-Verbose ("No Local IPs detected for this server") + } + } else { + Write-Host ("Script failed to retrieve local IPs for the server with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $FailedServersFilter += $Server + continue + } + + if ($resultsInvoke.IsBackUpSuccessful) { + Write-Host ("Successfully backed up IP filtering allow list") -ForegroundColor Green + } else { + Write-Host ("Script failed to backup IP filtering allow list with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $FailedServersFilter += $Server + continue + } + + if ($resultsInvoke.IsCreateIPRulesSuccessful) { + Write-Host ("Successfully updated IP filtering allow list") -ForegroundColor Green + if ($resultsInvoke.IPsNotAdded.Length -gt 0) { + $line = ("We didn't add the below IPs to the allow list as there are already existing rules present.`n{0}" -f (GetCommaSaperatedString -list $resultsInvoke.IPsNotAdded)) + Write-Warning $line + Write-Verbose $line + } + } else { + Write-Host ("Script failed to update IP filtering allow list with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $FailedServersFilter += $Server + continue + } + + if ($resultsInvoke.IsSetDefaultRuleSuccessful) { + Write-Host ("Successfully set the default IP filtering rule to deny") -ForegroundColor Green + } else { + Write-Host ("Script failed to set the default IP filtering rule to deny with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $FailedServersFilter += $Server + continue + } + } + } end { + if ($FailedServersEP.Length -gt 0) { + Write-Host ("Unable to Turn Off Extended Protection on the following servers: {0}" -f [string]::Join(", ", $FailedServersEP)) -ForegroundColor Red + } + + if ($FailedServersFilter.Length -gt 0) { + Write-Host ("Unable to create IP Filtering Rules on the following servers: {0}" -f [string]::Join(", ", $FailedServersFilter)) -ForegroundColor Red + } + } +} diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 new file mode 100644 index 0000000000..5e3683e619 --- /dev/null +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -0,0 +1,140 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +. $PSScriptRoot\..\..\..\..\Shared\Invoke-ScriptBlockHandler.ps1 +. $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 + +$RollbackIPFiltering = { + param( + [Object]$Arguments + ) + + $Site = $Arguments.Site + $VDir = $Arguments.VDir + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + + $SiteVDirLocation = $Site + if ($VDir -ne '') { + $SiteVDirLocation += '/' + $VDir + } + + $results = @{ + BackUpPath = $null + BackupCurrentSuccessful = $false + RestorePath = $null + RestoreSuccessful = $false + ErrorContext = $null + } + + function Backup-currentIpFilteringRules { + param( + $BackupPath + ) + + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + + return $true + } + + function Restore-OriginalIpFilteringRules { + param( + $OriginalIpFilteringRules, + $DefaultForUnspecifiedIPs + ) + + Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop + $RulesToBeAdded = @() + foreach ($IpFilteringRule in $OriginalIpFilteringRules) { + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } + } + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop + + return $true + } + + try { + $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/','-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath + + $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/','-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName + $originalIpFilteringConfigurations = (Get-Content $results.RestorePath | Out-String | ConvertFrom-Json) + $results.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) + } catch { + $results.ErrorContext = $_ + } + + return $results +} + +function Invoke-RollbackIPFiltering { + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [object[]]$ExchangeServers, + [Parameter(Mandatory = $true)] + [string]$Site, + [Parameter(Mandatory = $true)] + [string]$VDir + ) + + begin { + $FailedServers = New-Object 'System.Collections.Generic.List[string]' + + $progressParams = @{ + Activity = "Rolling back IP filtering Rules" + Status = [string]::Empty + PercentComplete = 0 + } + + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + } process { + $scriptblockArgs = [PSCustomObject]@{ + Site = $Site + VDir = $VDir + } + + $exchangeServersProcessed = 0 + $totalExchangeServers = $ExchangeServers.Count + foreach ($Server in $ExchangeServers) { + $baseStatus = "Processing: $($Server.Name) -" # Should this be at the start as the server is already processed? + $progressParams.PercentComplete = ($exchangeServersProcessed / $totalExchangeServers * 100) + $progressParams.Status = "$baseStatus Rolling back rules" + Write-Progress @progressParams + $exchangeServersProcessed++; + + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with Arguments Site: {1}, VDir: {2}" -f $Server.Name, $Site, $VDir) + Write-Host ("Restoring previous state for Server {0}" -f $Server.Name) + $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs + $Failed = $false + + if ($resultsInvoke.BackupCurrentSuccessful) { + Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" + if ($resultsInvoke.RestoreSuccessful) { + Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" -ForegroundColor Green + } else { + Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $Failed = $true + } + } else { + Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $Failed = $true + } + + if ($Failed) { + $FailedServers += $Server.Name + } + } + } end { + if ($FailedServers.Length -gt 0) { + Write-Host ("Unable to rollback for the following servers: {0}" -f [string]::Join(", ", $FailedServers)) -ForegroundColor Red + } + } +} diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 new file mode 100644 index 0000000000..b3ac507d2b --- /dev/null +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -0,0 +1,238 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +. $PSScriptRoot\..\..\..\..\Shared\Invoke-ScriptBlockHandler.ps1 +. $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 + +$ValidateMitigationScriptBlock = { + param( + [Object]$Arguments + ) + + $SiteVDirLocation = $Arguments.SiteVDirLocation + $IpRangesForFiltering = $Arguments.IpRangesForFiltering + + $results = @{ + IsEPVerified = $false + IsEPOff = $false + AreIPRulesVerified = $false + IsDefaultFilterVerified = $false + IsDefaultFilterDeny = $false + RulesNotFound = $null + ErrorContext = $null + } + + function Get-LocalIpAddresses { + $ips = @() + $interfaces = Get-NetIPAddress + foreach ($interface in $interfaces) { + if ($interface.AddressState -eq 'Preferred') { + $ips += $interface.IPAddress + } + } + + return $ips + } + + # Set EP to None + function GetEPState { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation + ) + + $Filter = 'system.webServer/security/authentication/windowsAuthentication/extendedProtection' + + $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking + return $ExtendedProtection + } + + # Create ip allow list from user provided ip subnets + function VerifyIPRangeAllowList { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $true)] + [object[]]$IpFilteringRules, + [Parameter(Mandatory = $true)] + [hashtable]$results + ) + + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + + foreach ($IpFilteringRule in $IpFilteringRules) { + $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } + if ($null -eq $ExistingIPSubnetRule) { + if ($IpFilteringRule.Type -eq "Single IP") { + $IpString = $IpFilteringRule.IP + } else { + $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + } + $results.RulesNotFound += $IpString + } + } + + $results.AreIPRulesVerified = $true + + $results.IsDefaultFilterDeny = -not (Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted") + $results.IsDefaultFilterVerified = $true + return $results + } + + try { + $EPState = GetEPState -SiteVDirLocation $SiteVDirLocation + + if ($EPState -eq "None") { + $results.IsEPOff = $true + } else { + $results.IsEPOff = $false + } + + $results.IsEPVerified = $true + + if ($IpRangesForFiltering -ne $null) { + $localIPs = Get-LocalIpAddresses + + $localIPs | ForEach-Object { + $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } + } + + VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results + } + } catch { + $results.ErrorContext = $_ + } + + return $results +} + +function GetCommaSaperatedString { + param( + $list + ) + + $string = "" + foreach ($element in $list) { + $string += ($element.ToString() + ", ") + } + + return $string.Trim(", ") +} + +function Invoke-ValidateMitigation { + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string[]]$ExchangeServers, + [Parameter(Mandatory = $false)] + $ipRangeAllowListRules, + [Parameter(Mandatory = $true)] + [string]$Site, + [Parameter(Mandatory = $true)] + [string]$VDir + ) + + begin { + $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' + $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' + + $UnMitigatedServersEP = New-Object 'System.Collections.Generic.List[string]' + $UnMitigatedServersFilter = New-Object 'System.Collections.Generic.List[string]' + + $progressParams = @{ + Activity = "Verifying Mitigations" + Status = [string]::Empty + PercentComplete = 0 + } + + $ShouldVerifyFilter = ($null -ne $ipRangeAllowListRules) + + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + } process { + $SiteVDirLocation = $Site + if ($VDir -ne '') { + $SiteVDirLocation += '/' + $VDir + } + + $scriptblockArgs = [PSCustomObject]@{ + SiteVDirLocation = $SiteVDirLocation + IpRangesForFiltering = $ipRangeAllowListRules + } + + $counter = 0 + $totalCount = $ExchangeServers.Count + foreach ($Server in $ExchangeServers) { + $baseStatus = "Processing: $Server -" + $progressParams.PercentComplete = ($counter / $totalCount * 100) + $progressParams.Status = "$baseStatus Applying rules" + Write-Progress @progressParams + $counter ++; + + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, [string]::Join(", ", $ipRangeAllowListRules)) + Write-Host ("Validating Mitigations on Server {0}" -f $Server) + $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs + + if ($resultsInvoke.IsEPOff) { + Write-Host ("Expected: The state of Extended protection flag is None") -ForegroundColor Green + } elseif ($resultsInvoke.IsEPVerified) { + Write-Host ("Unexpected: The state of Extended protection flag is not set to None") -ForegroundColor Red + $UnMitigatedServersEP += $Server + } else { + Write-Host ("Unknown: Script failed to get state of Extended protection flag with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersEP += $Server + $FailedServersFilter += $Server + continue + } + + if (-not $ShouldVerifyFilter) { + continue + } + + if ($resultsInvoke.AreIPRulesVerified) { + Write-Host ("Unknown: Script failed to verify IP Filtering Rules with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersFilter += $Server + continue + } elseif ($null -eq $resultsInvoke.RulesNotFound -and $resultsInvoke.RulesNotFound -gt 0) { + Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied") -ForegroundColor Red + Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.RulesNotFound)) + $UnMitigatedServersFilter += $Server + } else { + Write-Host ("Expected: Successfully verified all the IP filtering rules") -ForegroundColor Green + } + + if ($resultsInvoke.IsDefaultFilterDeny) { + Write-Host ("Expected: The default IP Filtering rule is set to deny") -ForegroundColor Green + } elseif ($resultsInvoke.IsDefaultFilterVerified) { + Write-Host ("Unexpected: The default IP Filtering rule is not set to deny") -ForegroundColor Red + $UnMitigatedServersFilter += $Server + } else { + Write-Host ("Unknown: Script failed to get the default IP Filtering rule with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersFilter += $Server + continue + } + } + } end { + if ($UnMitigatedServersEP.Length -gt 0) { + Write-Host ("Extended Protection on the following servers are not set to expected values: {0}" -f [string]::Join(", ", $UnMitigatedServersEP)) -ForegroundColor Red + } + + if ($UnMitigatedServersFilter.Length -gt 0) { + Write-Host ("IP Filtering Rules or Default IP rule on the following servers is not as expected: {0}" -f [string]::Join(", ", $UnMitigatedServersFilter)) -ForegroundColor Red + } + + if ($FailedServersEP.Length -gt 0) { + Write-Host ("Unable to verify Extended Protection on the following servers: {0}" -f [string]::Join(", ", $FailedServersEP)) -ForegroundColor Red + } + + if ($FailedServersFilter.Length -gt 0) { + Write-Host ("Unable to verify IP Filtering Rules on the following servers: {0}" -f [string]::Join(", ", $FailedServersFilter)) -ForegroundColor Red + } + } +} diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 new file mode 100644 index 0000000000..cc92294f76 --- /dev/null +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -0,0 +1,77 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +. $PSScriptRoot\..\..\..\Diagnostics\HealthChecker\DataCollection\ServerInformation\Get-AllNicInformation.ps1 + +# This function is used to get a list of all the IP in use by the Exchange Servers accross the topology +function Get-ExchangeServerIPs { + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [string]$OutputFilePath + ) + + begin { + $IPs = @() + $FailedServers = @() + + $progressParams = @{ + Activity = "Getting List of IPs in use by Exchange Servers" + Status = [string]::Empty + PercentComplete = 0 + } + + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + } + process { + try { + $ExchangeServers = Get-ExchangeServer + } catch { + Write-Host ("Unable to run Get-ExchangeServer due to: Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $_ + exit + } + + $counter = 0 + $totalCount = $ExchangeServers.Count + + foreach ($Server in $ExchangeServers) { + $baseStatus = "Processing: $($Server.Name) -" + $progressParams.PercentComplete = ($counter / $totalCount * 100) + $progressParams.Status = "$baseStatus Getting IPs" + Write-Progress @progressParams + + $HostNetworkInfo = Get-AllNicInformation -ComputerName $Server.Name -ComputerFQDN $Server.FQDN + if ($null -ne $HostNetworkInfo) { + if ($null -ne $HostNetworkInfo.IPv4Addresses) { + foreach ($address in $HostNetworkInfo.IPv4Addresses) { + $IPs += $address.Address + } + } + if ($null -ne $HostNetworkInfo.IPv6Addresses) { + foreach ($address in $HostNetworkInfo.IPv6Addresses) { + $IPs += $address.Address + } + } + } else { + $FailedServers += $Server.Name + Write-Verbose "Ip of $($Server.Name) cannot be found and will not be added to ip allow list." -ForegroundColor Red + } + + $counter++ + } + } + end { + if ($FailedServers -gt 0) { + Write-Host ("Unable to get IPs from the following servers: {0}" -f [string]::Join(", ", $FailedServers)) -ForegroundColor Red + } + + try { + $IPs | Out-File $OutputFilePath + Write-Host ("Please find the collected IPs at {0}" -f $OutputFilePath) + } catch { + Write-Host "Unable to write to file. Please check the path provided." -ForegroundColor Red + } + } +} diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 new file mode 100644 index 0000000000..59f44f7a8a --- /dev/null +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 @@ -0,0 +1,35 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# This function is used to collect the required information needed to determine if a server is ready for IP Filtering mitigation +function Get-IPFilteringPrerequisitesCheck { + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [object[]]$ExchangeServers + ) + begin { + $results = New-Object 'System.Collections.Generic.List[object]' + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + } process { + foreach ($server in $ExchangeServers) { + Write-Verbose ("Performing prereq checks on server: {0}" -f $server.ToString()) + + $computerResult = Invoke-ScriptBlockHandler -ComputerName $server.ToString() -ScriptBlock { return $env:COMPUTERNAME } + $serverConnected = $null -ne $computerResult + + if ($serverConnected) { + Write-Verbose ("Server {0} appears to up and reachable" -f $server.ToString()) + } else { + Write-Verbose ("Server {0} doesn't appear to be online." -f $server.ToString()) + } + + $results.Add([PSCustomObject]@{ + ComputerName = $server.ToString() + ServerOnline = $serverConnected + }) + } + } end { + return $results + } +} diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 new file mode 100644 index 0000000000..13fe5d9cf2 --- /dev/null +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -0,0 +1,108 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +. $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 + +# This function is used to get a list of all the IP in use by the Exchange Servers accross the topology +function Get-IPRangeAllowListFromFile { + [CmdletBinding()] + [OutputType([hashtable])] + param( + [Parameter(Mandatory = $true)] + [string]$FilePath + ) + + begin { + $results = @{ + ipRangeAllowListRules = @() + IsError = $false + } + + Write-Verbose "Calling: $($MyInvocation.MyCommand)" + } + process { + $FilePath = $FilePath.Trim('"', "'") + $IsPathValid = Test-Path -Path $FilePath + + if ($IsPathValid -eq $false) { + Write-Host "The path to file specified is invalid. Please provide a valid path." -ForegroundColor Red + $results.IsError = $true + return + } + + try { + $SubnetStrings = (Get-Content -Path $FilePath) + } catch { + Write-Host "Unable to read the content of specified file. Inner Exception" -ForegroundColor Red + Write-HostErrorInformation $_ + $results.IsError = $true + return + } + + <#if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { + Write-Host "The provided file is empty." + return + }#> + + # Log all the IPs present in the txt file supplied by user + Write-Verbose ("Read the contents of the file Successfully. List of IP ranges received from user: {0}" -f [string]::Join(", ", $SubnetStrings)) + + Write-Verbose "Validating the IP ranges specified in the file" + try { + foreach ($SubnetString in $SubnetStrings) { + $SubnetString = $SubnetString.Trim() + if ([string]::IsNullOrEmpty($SubnetString)) { + continue + } + + $IpAddressString = $SubnetString.Split("/")[0] + $SubnetMaskString = $SubnetString.Split("/")[1] + + # Check the type of IP address (IPv4/IPv6) + $IsIPv6 = $false + $IpAddress = $IpAddressString -as [IPAddress] + + if ($null -eq $IpAddress -or ($IpAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetwork -and $IpAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetworkV6)) { + # Invalid IP address found + Write-Host ("Invalid IP address: {0} found in CSV." -f $IpAddressString) -ForegroundColor Red + $results.IsError = $true + return + } elseif ($IpAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6) { + $IsIPv6 = $true; + } + + $IsSubnetMaskPresent = [Bool]$SubnetMaskString + + if ($IsSubnetMaskPresent) { + # Check if the subnet value is valid (IPv4 <= 32, IPv6 <= 128 or empty) + $SubnetMask = $SubnetMaskString -as [int] + if ($null -eq $SubnetMask) { + Write-Host ("Invalid Subnet Mask found: Unable to parse Subnet Mask {0}. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red + $results.IsError = $true + return + } elseif (($SubnetMask -gt 32 -and -not $IsIPv6) -or $SubnetMask -gt 128 -or $SubnetMask -lt 0) { + Write-Host ("Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red + $results.IsError = $true + return + } + + $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } + } else { + if ($IsIPv6) { + $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; SubnetMask="128"; Allowed=$true } + } else { + $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; SubnetMask="32"; Allowed=$true } + } + } + } + } catch { + Write-Host ("Unable to create IP allow rules. Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $_ + $results.IsError = $true + return + } + } + end { + return $results + } +} diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index b9e340ec89..d7fd77248f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -26,24 +26,54 @@ #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param( - [Parameter (Mandatory = $false, ValueFromPipeline, HelpMessage = "Enter the list of server names on which the script should execute on")] + [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enter the list of server names on which the script should execute on")] + [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ValidateMitigation', HelpMessage = "Enter the list of server names on which the script should execute on")] + [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ConfigureEP', HelpMessage = "Enter the list of server names on which the script should execute on")] [string[]]$ExchangeServerNames = $null, - [Parameter (Mandatory = $false, HelpMessage = "Enter the list of servers on which the script should not execute on")] + + [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enter the list of servers on which the script should not execute on")] + [Parameter (Mandatory = $false, ParameterSetName = 'ValidateMitigation', HelpMessage = "Enter the list of servers on which the script should not execute on")] + [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Enter the list of servers on which the script should not execute on")] [string[]]$SkipExchangeServerNames = $null, - [Parameter (Mandatory = $false, HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] + + [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] [switch]$ShowExtendedProtection, - [Parameter (Mandatory = $false, HelpMessage = "Used for internal options")] + + [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Used for internal options")] [string]$InternalOption, - [Parameter (Mandatory = $false, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] - [ValidateSet("RestoreIISAppConfig")] + + [Parameter (Mandatory = $true, ParameterSetName = 'GetExchangeIPs', HelpMessage = "Using this parameter will allow you to get the list of IPs used by Exchange Servers.")] + [switch]$FindExchangeServerIPAddresses, + + [Parameter (Mandatory = $false, ParameterSetName = 'GetExchangeIPs', HelpMessage = "Using this parameter will allow you to specify the path to the output file.")] + [string]$OutputFilePath, + + [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to apply IP filters.")] + [Parameter (Mandatory = $false, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to validate IP filters.")] + [string]$IPRange, + + [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir on which you want to configure mitigation.")] + [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir for which you want to validate mitigation.")] + [string]$RestrictType, + + [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this switch will allow you to validate if the mitigations have been applied correctly.")] + [switch]$ValidateMitigation, + + [Parameter (Mandatory = $true, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [string]$RollbackType ) begin { . $PSScriptRoot\Write-Verbose.ps1 . $PSScriptRoot\WriteFunctions.ps1 + . $PSScriptRoot\ConfigurationAction\Invoke-ConfigureMitigation.ps1 + . $PSScriptRoot\ConfigurationAction\Invoke-ValidateMitigation.ps1 + . $PSScriptRoot\ConfigurationAction\Invoke-RollbackIPFiltering.ps1 . $PSScriptRoot\ConfigurationAction\Invoke-ConfigureExtendedProtection.ps1 . $PSScriptRoot\ConfigurationAction\Invoke-RollbackExtendedProtection.ps1 + . $PSScriptRoot\DataCollection\Get-IPFilteringPrerequisitesCheck.ps1 + . $PSScriptRoot\DataCollection\Get-ExchangeServerIPs.ps1 + . $PSScriptRoot\DataCollection\Get-IPRangeAllowListFromFile.ps1 . $PSScriptRoot\DataCollection\Get-ExtendedProtectionPrerequisitesCheck.ps1 . $PSScriptRoot\DataCollection\Invoke-ExtendedProtectionTlsPrerequisitesCheck.ps1 . $PSScriptRoot\..\..\..\Shared\ScriptUpdateFunctions\Test-ScriptVersion.ps1 @@ -52,13 +82,92 @@ begin { . $PSScriptRoot\..\..\..\Shared\LoggerFunctions.ps1 . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 + enum MitigationTypes { + OnlyEP + Full + } + + $RestrictTypeToSiteVDirMap = @{ + "APIFrontend" ="Default Web Site/API" + "AutodiscoverFrontend" ="Default Web Site/Autodiscover" + "ECPFrontend" ="Default Web Site/ECP" + "EWSFrontend" ="Default Web Site/EWS" + "Microsoft-Server-ActiveSyncFrontend" ="Default Web Site/Microsoft-Server-ActiveSync" + "OABFrontend" ="Default Web Site/OAB" + "PowershellFrontend" ="Default Web Site/Powershell" + "OWAFrontend" ="Default Web Site/OWA" + "RPCFrontend" ="Default Web Site/RPC" + "MAPIFrontend" ="Default Web Site/MAPI" + "APIBackend" ="Exchange Back End/API" + "AutodiscoverBackend" ="Exchange Back End/Autodiscover" + "ECPBackend" ="Exchange Back End/ECP" + "EWSBackend" ="Exchange Back End/EWS" + "Microsoft-Server-ActiveSyncBackend" ="Exchange Back End/Microsoft-Server-ActiveSync" + "OABBackend" ="Exchange Back End/OAB" + "PowershellBackend" ="Exchange Back End/Powershell" + "OWABackend" ="Exchange Back End/OWA" + "RPCBackend" ="Exchange Back End/RPC" + "PushNotificationsBackend" ="Exchange Back End/PushNotifications" + "RPCWithCertBackend" ="Exchange Back End/RPCWithCert" + "MAPI-emsmdbBackend" ="Exchange Back End/MAPI/emsmdb" + "MAPI-nspiBackend" ="Exchange Back End/MAPI/nspi" + } + $RollbackSelected = $false + $RollbackRestrictType = $false + $ConfigureEPSelected = $false + $ConfigureMitigationSelected = $false + $Script:SkipEWS = $false + $includeExchangeServerNames = New-Object 'System.Collections.Generic.List[string]' if ($PsCmdlet.ParameterSetName -eq "Rollback") { $RollbackSelected = $true + if (-not ($RollbackType -eq "RestoreIISAppConfig" -or $RollbackType.StartsWith("RestrictType"))) { + Write-Host "Please provide a valid value of RollbackType" -ForegroundColor Red + return + } + if ($RollbackType -eq "RestoreIISAppConfig") { $RollbackRestoreIISAppConfig = $true + } else { + $RollbackRestrictType = $true + $RestrictType = $RollbackType.Split("RestrictType")[1] + $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] + $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } } + + if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation") { + if ($RestrictTypeToSiteVDirMap.ContainsKey($RestrictType)) { + $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] + $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] + } else { + Write-Host "The value specified for RestrictType is not valid." -ForegroundColor Red + return + } + + if ($PSBoundParameters.ContainsKey("IPRange")) { + $MitigationTypeSelected = [MitigationTypes]::Full + } else { + $MitigationTypeSelected = [MitigationTypes]::OnlyEP + } + + if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { + $ConfigureMitigationSelected = $true + } else { + $ValidateMitigation = $true + } + } + + if ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) { + $ConfigureEPSelected = $true + } + + if ($InternalOption -eq "SkipEWS") { + Write-Verbose "SkipEWS option enabled." + $Script:SkipEWS = $true + } else { + $Script:SkipEWS = $false + } } process { foreach ($server in $ExchangeServerNames) { $includeExchangeServerNames.Add($server) @@ -77,13 +186,6 @@ begin { SetWriteHostAction ${Function:Write-HostLog} - if ($InternalOption -eq "SkipEWS") { - Write-Verbose "SkipEWS option enabled." - $Script:SkipEWS = $true - } else { - $Script:SkipEWS = $false - } - if (-not((Confirm-ExchangeShell -Identity $env:COMPUTERNAME).ShellLoaded)) { Write-Warning "Failed to load the Exchange Management Shell. Start the script using the Exchange Management Shell." exit @@ -97,8 +199,16 @@ begin { return } - if (-not($RollbackSelected) -and - -not($ShowExtendedProtection)) { + if ($FindExchangeServerIPAddresses) { + if ([String]::IsNullOrEmpty($OutputFilePath)) { + $OutputFilePath = [System.IO.Path]::Combine((Get-Location).Path, "IPList.txt") + } + + Get-ExchangeServerIPs -OutputFilePath $OutputFilePath + return + } + + if ($ConfigureEPSelected) { $params = @{ Message = "Display Warning about Extended Protection" Target = "Extended Protection is recommended to be enabled for security reasons. " + @@ -114,6 +224,8 @@ begin { Show-Disclaimer @params } + # TODO : Show some disclaimer for mitigation script + Write-Verbose ("Running Get-ExchangeServer to get list of all exchange servers") Set-ADServerSettings -ViewEntireForest $true $ExchangeServers = Get-ExchangeServer | Where-Object { $_.AdminDisplayVersion -like "Version 15*" -and $_.ServerRole -ne "Edge" } @@ -169,7 +281,15 @@ begin { return } - if (-not($RollbackSelected)) { + if ($ConfigureEPSelected -or $ConfigureMitigationSelected -or $ValidateMitigation) { + if ($ConfigureMitigationSelected) { + Write-Host "Mitigations will only be applied on the servers which pass the Extended Protection Prerequisite Check" + } + + if ($ValidateMitigation) { + Write-Host "Mitigations will only be verified on the servers which pass the Extended Protection Prerequisite Check" + } + $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS if ($null -ne $prerequisitesCheck) { @@ -350,26 +470,70 @@ begin { Write-Warning "Failed to get Extended Protection Prerequisites Information to be able to continue" exit } - } else { + + if ($ConfigureEPSelected) { + # Configure Extended Protection based on given parameters + # Prior to executing, add back any unsupported versions back into the list + # for onlineSupportedServers, because the are online and we want to revert them. + $unsupportedAndConfiguredServers | ForEach-Object { $onlineSupportedServers.Add($_) } + $extendedProtectionConfigurations = ($onlineSupportedServers | + Where-Object { $_.ComputerName -in $serverNames }).ExtendedProtectionConfiguration + + if ($null -ne $extendedProtectionConfigurations) { + Invoke-ConfigureExtendedProtection -ExtendedProtectionConfigurations $extendedProtectionConfigurations + } else { + Write-Host "No servers are online or no Exchange Servers Support Extended Protection." + } + } + + if ($ConfigureMitigationSelected) { + if ($MitigationTypeSelected -eq [MitigationTypes]::Full) { + # Get list of IPs in object form from the file specified + $results = Get-IPRangeAllowListFromFile -FilePath $IPRange + + if ($results.IsError) { + exit + } + + $ipRangeAllowListRules = $results.ipRangeAllowListRules + # Apply rules + Invoke-ConfigureMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + } else { + Invoke-ConfigureMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $null -Site $Site -VDir $VDir + } + + return + } + + if ($ValidateMitigation) { + if ($MitigationTypeSelected -eq [MitigationTypes]::Full) { + # Get list of IPs in object form from the file specified + $results = Get-IPRangeAllowListFromFile -FilePath $IPRange + + if ($results.IsError) { + exit + } + + $ipRangeAllowListRules = $results.ipRangeAllowListRules + # Validate mitigation + Invoke-ValidateMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + } else { + Invoke-ValidateMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $null -Site $Site -VDir $VDir + } + + return + } + } elseif ($RollbackSelected) { Write-Host "Prerequisite check will be skipped due to Rollback" if ($RollbackRestoreIISAppConfig) { Invoke-RollbackExtendedProtection -ExchangeServers $ExchangeServers } - return - } - # Configure Extended Protection based on given parameters - # Prior to executing, add back any unsupported versions back into the list - # for onlineSupportedServers, because the are online and we want to revert them. - $unsupportedAndConfiguredServers | ForEach-Object { $onlineSupportedServers.Add($_) } - $extendedProtectionConfigurations = ($onlineSupportedServers | - Where-Object { $_.ComputerName -in $serverNames }).ExtendedProtectionConfiguration - - if ($null -ne $extendedProtectionConfigurations) { - Invoke-ConfigureExtendedProtection -ExtendedProtectionConfigurations $extendedProtectionConfigurations - } else { - Write-Host "No servers are online or no Exchange Servers Support Extended Protection." + if ($RollbackRestrictType) { + Invoke-RollbackIPFiltering -ExchangeServers $ExchangeServers -Site $Site -VDir $VDir + } + return } } finally { Write-Host "Do you have feedback regarding the script? Please email ExToolsFeedback@microsoft.com." From 1701d8938ab4db66b0a41693f1fbdf32df3a225b Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Tue, 9 Aug 2022 23:10:51 +0530 Subject: [PATCH 02/65] Fixing Get-AllNIcInformation inclusion path error --- .../DataCollection/Get-ExchangeServerIPs.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 index cc92294f76..9278d46f6f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. -. $PSScriptRoot\..\..\..\Diagnostics\HealthChecker\DataCollection\ServerInformation\Get-AllNicInformation.ps1 +. $PSScriptRoot\..\..\..\..\Diagnostics\HealthChecker\DataCollection\ServerInformation\Get-AllNicInformation.ps1 # This function is used to get a list of all the IP in use by the Exchange Servers accross the topology function Get-ExchangeServerIPs { From ec9655925d0178687572bae9993fe456d20a59bf Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 10 Aug 2022 01:44:12 +0530 Subject: [PATCH 03/65] Update: 1. Now only rules that have deny permissions already set will show up in IPsNotAdded 2. Updated the Warning message that is shown during apply mitigation and looged the IpsNotAdded in log file instead of prining it to console Bug fixes: 1. 255.255.255.255 entry getting added while rollback 2. extra True value getting added to the list of IPsNotAddded while applying mitigation 3. Apply mitigation fails with error duplicate ips found (some local ips getting duplicated) 4. fails to apply mitigation if duplicate ips are present in the ip list provided by user --- .../Invoke-ConfigureMitigation.ps1 | 36 +++++++++++-------- .../Invoke-RollbackIPFiltering.ps1 | 10 +++++- .../Get-IPRangeAllowListFromFile.ps1 | 11 +++--- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index da41172594..ee5f9a8263 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -35,7 +35,12 @@ $ConfigureMitigation = { $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + if ($null -eq $ExistingRules -or $ExistingRules.Length -eq 0) { + $BackupFilteringConfiguration = @{DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + } else { + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath return $true @@ -82,9 +87,7 @@ $ConfigureMitigation = { ) $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/','-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" - - Backup-currentIpFilteringRules -BackupPath $backupPath - $results.IsBackUpSuccessful = $true + $results.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' @@ -104,15 +107,15 @@ $ConfigureMitigation = { $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; } } } else { - if ($IpFilteringRule.Type -eq "Single IP") { - $IpString = $IpFilteringRule.IP - # Write-Verbose ("Unable to add allow rule for ip address: {0} as an already existing rule exist for this." -f $IpFilteringRule.IP ) - } else { - $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) - # Write-Verbose ("Unable to add allow rule for ip subnet: {0}/{1} as an already existing rule exist for this." -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask ) + if ($ExistingIPSubnetRule.allowed -ne $IpFilteringRule.Allowed) { + if ($IpFilteringRule.Type -eq "Single IP") { + $IpString = $IpFilteringRule.IP + } else { + $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + } + + $IPsNotAdded += $IpString } - - $IPsNotAdded += $IpString } } @@ -141,8 +144,10 @@ $ConfigureMitigation = { $localIPs = Get-LocalIpAddresses $results.IsGetLocalIPSuccessful = $true - $localIPs | ForEach-Object { - $IpRangesForFiltering += @{Type="Single IP"; IP=$_; SubnetMask=$null; Allowed=$true } + ForEach ($localIP in $localIPs) { + if ($null -eq ($IpRangesForFiltering | Where-Object {$_.Type -eq "Single IP" -and $_.IP -eq $localIP -and $_.Allowed -eq $true})) { + $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } + } } $results.LocalIPs = $localIPs @@ -269,9 +274,10 @@ function Invoke-ConfigureMitigation { if ($resultsInvoke.IsCreateIPRulesSuccessful) { Write-Host ("Successfully updated IP filtering allow list") -ForegroundColor Green if ($resultsInvoke.IPsNotAdded.Length -gt 0) { - $line = ("We didn't add the below IPs to the allow list as there are already existing rules present.`n{0}" -f (GetCommaSaperatedString -list $resultsInvoke.IPsNotAdded)) + $line = ("We didn't add a few IPs to the allow list because deny rules for these IPs are already present.") Write-Warning $line Write-Verbose $line + Write-Verbose (GetCommaSaperatedString -list $resultsInvoke.IPsNotAdded) } } else { Write-Host ("Script failed to update IP filtering allow list with the Inner Exception:") -ForegroundColor Red diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 5e3683e619..6d2eb97852 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -32,9 +32,17 @@ $RollbackIPFiltering = { $BackupPath ) + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + if ($null -eq $ExistingRules -or $ExistingRules.Length -eq 0) { + $BackupFilteringConfiguration = @{DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + } else { + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath return $true diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index 13fe5d9cf2..ff2a1acda0 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -85,13 +85,12 @@ function Get-IPRangeAllowListFromFile { $results.IsError = $true return } - - $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } + if ($null -eq ($results.ipRangeAllowListRules | Where-Object {$_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString -and $_.Allowed -eq $true})) { + $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } + } } else { - if ($IsIPv6) { - $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; SubnetMask="128"; Allowed=$true } - } else { - $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; SubnetMask="32"; Allowed=$true } + if ($null -eq ($results.ipRangeAllowListRules | Where-Object {$_.Type -eq "Single IP" -and $_.IP -eq $IpAddressString -and $_.Allowed -eq $true})) { + $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; Allowed=$true } } } } From 3bd28671eed7771d56c074c76b50032454f6d88e Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 02:31:20 +0530 Subject: [PATCH 04/65] Flag changes, logging improvement, bug fixes --- .../Invoke-ConfigureMitigation.ps1 | 43 ++++++------ .../Invoke-RollbackIPFiltering.ps1 | 52 ++++++++------- .../ExchangeExtendedProtectionManagement.ps1 | 66 +++++++++++-------- 3 files changed, 90 insertions(+), 71 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index ee5f9a8263..aab6edf0b6 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -12,6 +12,8 @@ $ConfigureMitigation = { $SiteVDirLocation = $Arguments.SiteVDirLocation $IpRangesForFiltering = $Arguments.IpRangesForFiltering + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' $results = @{ IsTurnOffEPSuccessful = $false @@ -30,20 +32,15 @@ $ConfigureMitigation = { $BackupPath ) - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' - - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection - $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - if ($null -eq $ExistingRules -or $ExistingRules.Length -eq 0) { - $BackupFilteringConfiguration = @{DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - } else { - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - } - - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath - - return $true + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + if ($null -eq $ExistingRules){ + $ExistingRules = @() + } + + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + return $true } function Get-LocalIpAddresses { @@ -73,6 +70,8 @@ $ConfigureMitigation = { if ($ExtendedProtection -ne "None") { Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" } + + return $true } # Create ip allow list from user provided ip subnets @@ -130,8 +129,7 @@ $ConfigureMitigation = { } try { - TurnOffEP -SiteVDirLocation $SiteVDirLocation - $results.IsTurnOffEPSuccessful = $true + $results.IsTurnOffEPSuccessful = TurnOffEP -SiteVDirLocation $SiteVDirLocation if ($null -ne $IpRangesForFiltering) { $installResult = Install-WindowsFeature Web-IP-Security @@ -221,7 +219,12 @@ function Invoke-ConfigureMitigation { Write-Progress @progressParams $counter ++; - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, [string]::Join(", ", $ipRangeAllowListRules)) + if($ipRangeAllowListRules -eq $null){ + $ipRangeAllowListString = "null" + } else { + $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) + } + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) Write-Host ("Applying Mitigations on Server {0}" -f $Server) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs @@ -273,9 +276,9 @@ function Invoke-ConfigureMitigation { if ($resultsInvoke.IsCreateIPRulesSuccessful) { Write-Host ("Successfully updated IP filtering allow list") -ForegroundColor Green - if ($resultsInvoke.IPsNotAdded.Length -gt 0) { - $line = ("We didn't add a few IPs to the allow list because deny rules for these IPs are already present.") - Write-Warning $line + if ($resultsInvoke.IPsNotAdded.Length -gt 0) { + $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") + Write-Warning ($line + "Check logs for further details.") Write-Verbose $line Write-Verbose (GetCommaSaperatedString -list $resultsInvoke.IPsNotAdded) } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 6d2eb97852..18aa4ef3e4 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -20,6 +20,7 @@ $RollbackIPFiltering = { } $results = @{ + RestoreFileExists = $false BackUpPath = $null BackupCurrentSuccessful = $false RestorePath = $null @@ -32,20 +33,15 @@ $RollbackIPFiltering = { $BackupPath ) - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' - - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection - $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - if ($null -eq $ExistingRules -or $ExistingRules.Length -eq 0) { - $BackupFilteringConfiguration = @{DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - } else { - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - } - - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath - - return $true + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + if ($null -eq $ExistingRules){ + $ExistingRules = @() + } + + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + return $true } function Restore-OriginalIpFilteringRules { @@ -66,10 +62,15 @@ $RollbackIPFiltering = { } try { + $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/','-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName + if($results.RestorePath -eq $null){ + throw "Invalid operation. No backup file exisits at path $($env:WINDIR)\System32\inetsrv\config\" + } + $results.RestoreFileExists = $true + $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/','-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath - $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/','-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName $originalIpFilteringConfigurations = (Get-Content $results.RestorePath | Out-String | ConvertFrom-Json) $results.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) } catch { @@ -121,19 +122,24 @@ function Invoke-RollbackIPFiltering { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs $Failed = $false - if ($resultsInvoke.BackupCurrentSuccessful) { - Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" - if ($resultsInvoke.RestoreSuccessful) { - Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" -ForegroundColor Green + if($resultsInvoke.RestoreFileExists){ + if ($resultsInvoke.BackupCurrentSuccessful) { + Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" + if ($resultsInvoke.RestoreSuccessful) { + Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" -ForegroundColor Green + } else { + Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $Failed = $true + } } else { - Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $Failed = $true } } else { - Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext - $Failed = $true + Write-Host "No restore file exists on server $($Server.Name). Aborting rollback on the server $($Server.Name)." -ForegroundColor Red + $Failed = $true } if ($Failed) { diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index d7fd77248f..8cdcf9f54c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -57,7 +57,7 @@ param( [string]$RestrictType, [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this switch will allow you to validate if the mitigations have been applied correctly.")] - [switch]$ValidateMitigation, + [string]$ValidateMitigation, [Parameter (Mandatory = $true, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [string]$RollbackType @@ -82,11 +82,13 @@ begin { . $PSScriptRoot\..\..\..\Shared\LoggerFunctions.ps1 . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 + enum MitigationTypes { OnlyEP Full } + $SupportedRestrictTypes = @('EWSBackend') $RestrictTypeToSiteVDirMap = @{ "APIFrontend" ="Default Web Site/API" "AutodiscoverFrontend" ="Default Web Site/Autodiscover" @@ -112,37 +114,56 @@ begin { "MAPI-emsmdbBackend" ="Exchange Back End/MAPI/emsmdb" "MAPI-nspiBackend" ="Exchange Back End/MAPI/nspi" } + $RollbackSelected = $false $RollbackRestrictType = $false $ConfigureEPSelected = $false $ConfigureMitigationSelected = $false + $ValidateMitigationSelected = $false $Script:SkipEWS = $false $includeExchangeServerNames = New-Object 'System.Collections.Generic.List[string]' if ($PsCmdlet.ParameterSetName -eq "Rollback") { $RollbackSelected = $true - if (-not ($RollbackType -eq "RestoreIISAppConfig" -or $RollbackType.StartsWith("RestrictType"))) { - Write-Host "Please provide a valid value of RollbackType" -ForegroundColor Red - return - } if ($RollbackType -eq "RestoreIISAppConfig") { $RollbackRestoreIISAppConfig = $true - } else { + } elseif ($SupportedRestrictTypes -contains $RollbackType.Replace("RestrictType", "")) { $RollbackRestrictType = $true - $RestrictType = $RollbackType.Split("RestrictType")[1] + $RestrictType = $RollbackType.Replace("RestrictType", "") $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] + } else { + Write-Host "Please provide a valid value of RollbackType" -ForegroundColor Red + exit } } - if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation") { - if ($RestrictTypeToSiteVDirMap.ContainsKey($RestrictType)) { - $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] - $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] - } else { - Write-Host "The value specified for RestrictType is not valid." -ForegroundColor Red - return + + + if(($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")){ + + if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation"){ + if ($SupportedRestrictTypes -contains $RestrictType) { + $ConfigureMitigationSelected = $true + $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] + $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] + } else { + Write-Host "Please provide a valid value of RestrictType" -ForegroundColor Red + exit + } + } + + if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { + if($SupportedRestrictTypes -contains $ValidateMitigation.Replace("RestrictType", "")) { + $ValidateMitigationSelected = $true + $RestrictType = $ValidateMitigation.Replace("RestrictType", "") + $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] + $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] + } else { + Write-Host "Please provide a valid value of ValidateMitigation" -ForegroundColor Red + exit + } } if ($PSBoundParameters.ContainsKey("IPRange")) { @@ -150,13 +171,8 @@ begin { } else { $MitigationTypeSelected = [MitigationTypes]::OnlyEP } - - if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { - $ConfigureMitigationSelected = $true - } else { - $ValidateMitigation = $true - } } + if ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) { $ConfigureEPSelected = $true @@ -485,8 +501,7 @@ begin { Write-Host "No servers are online or no Exchange Servers Support Extended Protection." } } - - if ($ConfigureMitigationSelected) { + elseif ($ConfigureMitigationSelected) { if ($MitigationTypeSelected -eq [MitigationTypes]::Full) { # Get list of IPs in object form from the file specified $results = Get-IPRangeAllowListFromFile -FilePath $IPRange @@ -501,11 +516,8 @@ begin { } else { Invoke-ConfigureMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $null -Site $Site -VDir $VDir } - - return } - - if ($ValidateMitigation) { + elseif ($ValidateMitigationSelected) { if ($MitigationTypeSelected -eq [MitigationTypes]::Full) { # Get list of IPs in object form from the file specified $results = Get-IPRangeAllowListFromFile -FilePath $IPRange @@ -520,8 +532,6 @@ begin { } else { Invoke-ValidateMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $null -Site $Site -VDir $VDir } - - return } } elseif ($RollbackSelected) { Write-Host "Prerequisite check will be skipped due to Rollback" From bc5ba65da2b91e6cced1a248c5587accea379569 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 03:18:03 +0530 Subject: [PATCH 05/65] Use exchangeServer and skipExchangeServer flag during mitigation operations --- .../ExchangeExtendedProtectionManagement.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 8cdcf9f54c..8caf1972f4 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -512,9 +512,9 @@ begin { $ipRangeAllowListRules = $results.ipRangeAllowListRules # Apply rules - Invoke-ConfigureMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } else { - Invoke-ConfigureMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $null -Site $Site -VDir $VDir + Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $null -Site $Site -VDir $VDir } } elseif ($ValidateMitigationSelected) { @@ -528,9 +528,9 @@ begin { $ipRangeAllowListRules = $results.ipRangeAllowListRules # Validate mitigation - Invoke-ValidateMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } else { - Invoke-ValidateMitigation -ExchangeServers $onlineSupportedServers.ComputerName -ipRangeAllowListRules $null -Site $Site -VDir $VDir + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $null -Site $Site -VDir $VDir } } } elseif ($RollbackSelected) { From aeed75db615c92f092df9028c5b2bdd6517ec457 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 10 Aug 2022 03:53:55 +0530 Subject: [PATCH 06/65] Validate Mitigation bug fixes --- .../Invoke-ValidateMitigation.ps1 | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index b3ac507d2b..67c68ebef7 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -15,10 +15,12 @@ $ValidateMitigationScriptBlock = { $results = @{ IsEPVerified = $false IsEPOff = $false + IsWindowsFeatureInstalled = $false + IsWindowsFeatureVerified = $false AreIPRulesVerified = $false IsDefaultFilterVerified = $false IsDefaultFilterDeny = $false - RulesNotFound = $null + RulesNotFound = @() ErrorContext = $null } @@ -57,6 +59,13 @@ $ValidateMitigationScriptBlock = { [Parameter(Mandatory = $true)] [hashtable]$results ) + + $results.IsWindowsFeatureInstalled = (Get-WindowsFeature -Name "Web-IP-Security").InstallState -eq "Installed" + $results.IsWindowsFeatureVerified = $true + + if (-not $results.IsWindowsFeatureInstalled) { + return + } $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' @@ -77,9 +86,8 @@ $ValidateMitigationScriptBlock = { $results.AreIPRulesVerified = $true - $results.IsDefaultFilterDeny = -not (Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted") + $results.IsDefaultFilterDeny = -not ((Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted").Value) $results.IsDefaultFilterVerified = $true - return $results } try { @@ -172,7 +180,7 @@ function Invoke-ValidateMitigation { Write-Progress @progressParams $counter ++; - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, [string]::Join(", ", $ipRangeAllowListRules)) + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: list of length" -f $Server, $SiteVDirLocation, $ipRangeAllowListRules.Length.ToString()) Write-Host ("Validating Mitigations on Server {0}" -f $Server) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs @@ -193,29 +201,46 @@ function Invoke-ValidateMitigation { continue } - if ($resultsInvoke.AreIPRulesVerified) { - Write-Host ("Unknown: Script failed to verify IP Filtering Rules with Inner Exception") -ForegroundColor Red + $IsFilterUnMitigated = $false + + if (-not $resultsInvoke.IsWindowsFeatureVerified) { + Write-Host ("Unknown: Script failed to verify if the Windows feature Web-IP-Security is present with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext $FailedServersFilter += $Server continue - } elseif ($null -eq $resultsInvoke.RulesNotFound -and $resultsInvoke.RulesNotFound -gt 0) { - Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied") -ForegroundColor Red - Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.RulesNotFound)) - $UnMitigatedServersFilter += $Server + } elseif (-not $resultsInvoke.IsWindowsFeatureInstalled) { + Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server") -ForegroundColor Red + $IsFilterUnMitigated = $true } else { - Write-Host ("Expected: Successfully verified all the IP filtering rules") -ForegroundColor Green + Write-Host ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server") -ForegroundColor Green + if (-not $resultsInvoke.AreIPRulesVerified) { + Write-Host ("Unknown: Script failed to verify IP Filtering Rules with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersFilter += $Server + continue + } elseif ($null -ne $resultsInvoke.RulesNotFound -and $resultsInvoke.RulesNotFound.Length -gt 0) { + Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied") -ForegroundColor Red + Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.RulesNotFound)) + $IsFilterUnMitigated = $true + } else { + Write-Host ("Expected: Successfully verified all the IP filtering rules") -ForegroundColor Green + } + + if ($resultsInvoke.IsDefaultFilterDeny) { + Write-Host ("Expected: The default IP Filtering rule is set to deny") -ForegroundColor Green + } elseif ($resultsInvoke.IsDefaultFilterVerified) { + Write-Host ("Unexpected: The default IP Filtering rule is not set to deny") -ForegroundColor Red + $IsFilterUnMitigated = $true + } else { + Write-Host ("Unknown: Script failed to get the default IP Filtering rule with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersFilter += $Server + continue + } } - if ($resultsInvoke.IsDefaultFilterDeny) { - Write-Host ("Expected: The default IP Filtering rule is set to deny") -ForegroundColor Green - } elseif ($resultsInvoke.IsDefaultFilterVerified) { - Write-Host ("Unexpected: The default IP Filtering rule is not set to deny") -ForegroundColor Red + if ($IsFilterUnMitigated) { $UnMitigatedServersFilter += $Server - } else { - Write-Host ("Unknown: Script failed to get the default IP Filtering rule with Inner Exception") -ForegroundColor Red - Write-HostErrorInformation $results.ErrorContext - $FailedServersFilter += $Server - continue } } } end { From 547157150349bb236eba3b92d246ec0b3737568b Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 04:17:52 +0530 Subject: [PATCH 07/65] empty iprange file disclaimer --- .../Get-IPRangeAllowListFromFile.ps1 | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index ff2a1acda0..d5ef082de7 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -39,10 +39,19 @@ function Get-IPRangeAllowListFromFile { return } - <#if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { - Write-Host "The provided file is empty." - return - }#> + if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { + $SubnetStrings = @() + Write-Warning "The provided file is empty." + $params = @{ + Message = "Display Warning about using an empty ip file for ip filtering" + Target = "The file provided to create the ip filtering allow list is empty." + + " Using this will block all external inbound connections." + + "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc. Do you want to proceed?" + Operation = "Enabling IP Filtering Mitigation" + } + + Show-Disclaimer @params + } # Log all the IPs present in the txt file supplied by user Write-Verbose ("Read the contents of the file Successfully. List of IP ranges received from user: {0}" -f [string]::Join(", ", $SubnetStrings)) From bf74475331ff0a549c0ba60468e2b3ee912d3db1 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 11:45:04 +0530 Subject: [PATCH 08/65] Remove restrict type from ValidateMitiagtion parameter set --- .../ExchangeExtendedProtectionManagement.ps1 | 1 - 1 file changed, 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 8caf1972f4..e3fee5e3b5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -53,7 +53,6 @@ param( [string]$IPRange, [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir on which you want to configure mitigation.")] - [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir for which you want to validate mitigation.")] [string]$RestrictType, [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this switch will allow you to validate if the mitigations have been applied correctly.")] From 268c35edbc55fb300fd2557e0cb802cc32266c36 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 11:48:45 +0530 Subject: [PATCH 09/65] Fixing Validating rules status --- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 67c68ebef7..a1d4fdee30 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -176,7 +176,7 @@ function Invoke-ValidateMitigation { foreach ($Server in $ExchangeServers) { $baseStatus = "Processing: $Server -" $progressParams.PercentComplete = ($counter / $totalCount * 100) - $progressParams.Status = "$baseStatus Applying rules" + $progressParams.Status = "$baseStatus Validating rules" Write-Progress @progressParams $counter ++; From 917c1f9b43c797e23bebef5898a6505f29a012b7 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 14:28:09 +0530 Subject: [PATCH 10/65] IP limit check, empty ExchangeServer param, null reference issue fix --- .../Get-IPRangeAllowListFromFile.ps1 | 14 ++++++++++++-- .../ExchangeExtendedProtectionManagement.ps1 | 5 +++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index d5ef082de7..c116e76c74 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -31,7 +31,7 @@ function Get-IPRangeAllowListFromFile { } try { - $SubnetStrings = (Get-Content -Path $FilePath) + $SubnetStrings = (Get-Content -Path $FilePath) | ? {$_.trim() -ne "" } } catch { Write-Host "Unable to read the content of specified file. Inner Exception" -ForegroundColor Red Write-HostErrorInformation $_ @@ -51,10 +51,13 @@ function Get-IPRangeAllowListFromFile { } Show-Disclaimer @params + $ipRangesString = "{}" + } else { + $ipRangesString = [string]::Join(", ", $SubnetStrings) } # Log all the IPs present in the txt file supplied by user - Write-Verbose ("Read the contents of the file Successfully. List of IP ranges received from user: {0}" -f [string]::Join(", ", $SubnetStrings)) + Write-Verbose ("Read the contents of the file Successfully. List of IP ranges received from user: {0}" -f $ipRangesString) Write-Verbose "Validating the IP ranges specified in the file" try { @@ -103,6 +106,13 @@ function Get-IPRangeAllowListFromFile { } } } + + if($results.ipRangeAllowListRules.count -gt 500){ + Write-Host ("Too many IP filtering rules. Please reduce the specified entries by providing appropriate subnets." -f $SubnetMaskString) -ForegroundColor Red + $results.IsError = $true + return + } + } catch { Write-Host ("Unable to create IP allow rules. Inner Exception") -ForegroundColor Red Write-HostErrorInformation $_ diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index e3fee5e3b5..5824655406 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -258,6 +258,11 @@ begin { $ExchangeServers = $ExchangeServers | Where-Object { ($_.Name -notin $SkipExchangeServerNames) -and ($_.FQDN -notin $SkipExchangeServerNames) } } + if($ExchangeServers -eq $null){ + Write-Host "No exchange servers to process. Please specify server filters correctly" + exit + } + if ($ShowExtendedProtection) { Write-Verbose "Showing Extended Protection Information Only" $extendedProtectionConfigurations = New-Object 'System.Collections.Generic.List[object]' From a7e22a361543174110076a74d195fd754233b6ae Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 10 Aug 2022 14:47:52 +0530 Subject: [PATCH 11/65] Fixed issues with strings --- .../Invoke-ConfigureMitigation.ps1 | 19 +++++++++++-------- .../Invoke-RollbackIPFiltering.ps1 | 2 +- .../Invoke-ValidateMitigation.ps1 | 11 ++++++----- .../Get-IPRangeAllowListFromFile.ps1 | 10 +++++----- .../ExchangeExtendedProtectionManagement.ps1 | 4 ++-- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index aab6edf0b6..256bd3229f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -224,12 +224,13 @@ function Invoke-ConfigureMitigation { } else { $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) } - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) - Write-Host ("Applying Mitigations on Server {0}" -f $Server) + + $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs + Write-Host ("Setting Extended protection flag to None on Server {0}" -f $Server) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs if ($resultsInvoke.IsTurnOffEPSuccessful) { - Write-Host ("Successfully turned Off Extended Protection") -ForegroundColor Green + Write-Host ("Successfully turned Off Extended Protection") } else { Write-Host ("Script failed to Turn Off Extended protection with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -242,8 +243,10 @@ function Invoke-ConfigureMitigation { continue } + Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) + if ($resultsInvoke.IsWindowsFeatureInstalled) { - Write-Host ("Successfully installed windows feature - Web-IP-Security") -ForegroundColor Green + Write-Host ("Successfully installed windows feature - Web-IP-Security") } else { Write-Host ("Script failed to install windows feature - Web-IP-Security with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -252,7 +255,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsGetLocalIPSuccessful) { - Write-Host ("Successfully retrieved local IPs for the server") -ForegroundColor Green + Write-Host ("Successfully retrieved local IPs for the server") if ($null -ne $resultsInvoke.LocalIPs -and $resultsInvoke.LocalIPs.Length -gt 0) { Write-Verbose ("Local IPs detected for this server: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.LocalIPs)) } else { @@ -266,7 +269,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsBackUpSuccessful) { - Write-Host ("Successfully backed up IP filtering allow list") -ForegroundColor Green + Write-Host ("Successfully backed up IP filtering allow list") } else { Write-Host ("Script failed to backup IP filtering allow list with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -275,7 +278,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsCreateIPRulesSuccessful) { - Write-Host ("Successfully updated IP filtering allow list") -ForegroundColor Green + Write-Host ("Successfully updated IP filtering allow list") if ($resultsInvoke.IPsNotAdded.Length -gt 0) { $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") Write-Warning ($line + "Check logs for further details.") @@ -290,7 +293,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsSetDefaultRuleSuccessful) { - Write-Host ("Successfully set the default IP filtering rule to deny") -ForegroundColor Green + Write-Host ("Successfully set the default IP filtering rule to deny") } else { Write-Host ("Script failed to set the default IP filtering rule to deny with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 18aa4ef3e4..5a75532f99 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -126,7 +126,7 @@ function Invoke-RollbackIPFiltering { if ($resultsInvoke.BackupCurrentSuccessful) { Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" if ($resultsInvoke.RestoreSuccessful) { - Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" -ForegroundColor Green + Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" } else { Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index a1d4fdee30..ee45c6fcf1 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -181,11 +181,11 @@ function Invoke-ValidateMitigation { $counter ++; Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: list of length" -f $Server, $SiteVDirLocation, $ipRangeAllowListRules.Length.ToString()) - Write-Host ("Validating Mitigations on Server {0}" -f $Server) + Write-Host ("Validating state of Extended protection flag on Server {0}" -f $Server) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs if ($resultsInvoke.IsEPOff) { - Write-Host ("Expected: The state of Extended protection flag is None") -ForegroundColor Green + Write-Host ("Expected: The state of Extended protection flag is None") } elseif ($resultsInvoke.IsEPVerified) { Write-Host ("Unexpected: The state of Extended protection flag is not set to None") -ForegroundColor Red $UnMitigatedServersEP += $Server @@ -201,6 +201,7 @@ function Invoke-ValidateMitigation { continue } + Write-Host ("Validating IP restrictions on Server {0}" -f $Server) $IsFilterUnMitigated = $false if (-not $resultsInvoke.IsWindowsFeatureVerified) { @@ -212,7 +213,7 @@ function Invoke-ValidateMitigation { Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server") -ForegroundColor Red $IsFilterUnMitigated = $true } else { - Write-Host ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server") -ForegroundColor Green + Write-Host ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server") if (-not $resultsInvoke.AreIPRulesVerified) { Write-Host ("Unknown: Script failed to verify IP Filtering Rules with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext @@ -223,11 +224,11 @@ function Invoke-ValidateMitigation { Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.RulesNotFound)) $IsFilterUnMitigated = $true } else { - Write-Host ("Expected: Successfully verified all the IP filtering rules") -ForegroundColor Green + Write-Host ("Expected: Successfully verified all the IP filtering rules") } if ($resultsInvoke.IsDefaultFilterDeny) { - Write-Host ("Expected: The default IP Filtering rule is set to deny") -ForegroundColor Green + Write-Host ("Expected: The default IP Filtering rule is set to deny") } elseif ($resultsInvoke.IsDefaultFilterVerified) { Write-Host ("Unexpected: The default IP Filtering rule is not set to deny") -ForegroundColor Red $IsFilterUnMitigated = $true diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index c116e76c74..5844d841dd 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -25,7 +25,7 @@ function Get-IPRangeAllowListFromFile { $IsPathValid = Test-Path -Path $FilePath if ($IsPathValid -eq $false) { - Write-Host "The path to file specified is invalid. Please provide a valid path." -ForegroundColor Red + Write-Host "Input file name for provided for IPRange isn't valid. Rexecute the command with correct path for IPRange parameter." -ForegroundColor Red $results.IsError = $true return } @@ -33,7 +33,7 @@ function Get-IPRangeAllowListFromFile { try { $SubnetStrings = (Get-Content -Path $FilePath) | ? {$_.trim() -ne "" } } catch { - Write-Host "Unable to read the content of specified file. Inner Exception" -ForegroundColor Red + Write-Host "Unable to read the content of file provided for IPRange. Inner Exception" -ForegroundColor Red Write-HostErrorInformation $_ $results.IsError = $true return @@ -76,7 +76,7 @@ function Get-IPRangeAllowListFromFile { if ($null -eq $IpAddress -or ($IpAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetwork -and $IpAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetworkV6)) { # Invalid IP address found - Write-Host ("Invalid IP address: {0} found in CSV." -f $IpAddressString) -ForegroundColor Red + Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid IP address detected: {0}." -f $IpAddressString) -ForegroundColor Red $results.IsError = $true return } elseif ($IpAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6) { @@ -89,11 +89,11 @@ function Get-IPRangeAllowListFromFile { # Check if the subnet value is valid (IPv4 <= 32, IPv6 <= 128 or empty) $SubnetMask = $SubnetMaskString -as [int] if ($null -eq $SubnetMask) { - Write-Host ("Invalid Subnet Mask found: Unable to parse Subnet Mask {0}. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red + Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid Subnet Mask found: Unable to parse Subnet Mask {0}. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true return } elseif (($SubnetMask -gt 32 -and -not $IsIPv6) -or $SubnetMask -gt 128 -or $SubnetMask -lt 0) { - Write-Host ("Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red + Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true return } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 5824655406..d7968d18ed 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -303,11 +303,11 @@ begin { if ($ConfigureEPSelected -or $ConfigureMitigationSelected -or $ValidateMitigation) { if ($ConfigureMitigationSelected) { - Write-Host "Mitigations will only be applied on the servers which pass the Extended Protection Prerequisite Check" + Write-Host "IP Restrictions will only be applied on the servers which pass the Extended Protection Prerequisite Check" } if ($ValidateMitigation) { - Write-Host "Mitigations will only be verified on the servers which pass the Extended Protection Prerequisite Check" + Write-Host "IP Restrictions will only be validated on the servers which pass the Extended Protection Prerequisite Check" } $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS From 0745291f35c9a6aa042ae64f9f564a56b5316129 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 16:22:55 +0530 Subject: [PATCH 12/65] Remove Enum type, Check invalid ip file before prereq --- .../ExchangeExtendedProtectionManagement.ps1 | 38 +++++++------------ 1 file changed, 13 insertions(+), 25 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index d7968d18ed..0582350138 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -28,11 +28,13 @@ param( [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enter the list of server names on which the script should execute on")] [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ValidateMitigation', HelpMessage = "Enter the list of server names on which the script should execute on")] + [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ConfigureEP', HelpMessage = "Enter the list of server names on which the script should execute on")] [string[]]$ExchangeServerNames = $null, [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enter the list of servers on which the script should not execute on")] [Parameter (Mandatory = $false, ParameterSetName = 'ValidateMitigation', HelpMessage = "Enter the list of servers on which the script should not execute on")] + [Parameter (Mandatory = $false, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Enter the list of servers on which the script should not execute on")] [string[]]$SkipExchangeServerNames = $null, @@ -82,11 +84,6 @@ begin { . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 - enum MitigationTypes { - OnlyEP - Full - } - $SupportedRestrictTypes = @('EWSBackend') $RestrictTypeToSiteVDirMap = @{ "APIFrontend" ="Default Web Site/API" @@ -166,9 +163,16 @@ begin { } if ($PSBoundParameters.ContainsKey("IPRange")) { - $MitigationTypeSelected = [MitigationTypes]::Full + $MitigationTypeSelected = 'EWSOffAndIPMitigation' + # Get list of IPs in object form from the file specified + $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRange + if ($ipResults.IsError) { + exit + } + + $ipRangeAllowListRules = $ipResults.ipRangeAllowListRules } else { - $MitigationTypeSelected = [MitigationTypes]::OnlyEP + $MitigationTypeSelected = 'OnlyEWSOffMitigation' } } @@ -506,15 +510,7 @@ begin { } } elseif ($ConfigureMitigationSelected) { - if ($MitigationTypeSelected -eq [MitigationTypes]::Full) { - # Get list of IPs in object form from the file specified - $results = Get-IPRangeAllowListFromFile -FilePath $IPRange - - if ($results.IsError) { - exit - } - - $ipRangeAllowListRules = $results.ipRangeAllowListRules + if ($MitigationTypeSelected -eq 'EWSOffAndIPMitigation') { # Apply rules Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } else { @@ -522,15 +518,7 @@ begin { } } elseif ($ValidateMitigationSelected) { - if ($MitigationTypeSelected -eq [MitigationTypes]::Full) { - # Get list of IPs in object form from the file specified - $results = Get-IPRangeAllowListFromFile -FilePath $IPRange - - if ($results.IsError) { - exit - } - - $ipRangeAllowListRules = $results.ipRangeAllowListRules + if ($MitigationTypeSelected -eq 'EWSOffAndIPMitigation') { # Validate mitigation Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } else { From f9869faf30baaff5c4acaa22129b2c77ddf19048 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 17:07:58 +0530 Subject: [PATCH 13/65] Output expected value of flags --- .../ExchangeExtendedProtectionManagement.ps1 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 0582350138..351c44ed9f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -84,7 +84,8 @@ begin { . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 - $SupportedRestrictTypes = @('EWSBackend') + $SupportedVDirTypes = @('EWSBackend') + $SupportedRestrictTypes = $SupportedVDirTypes | ForEach-Object {"RestrictType$_"} $RestrictTypeToSiteVDirMap = @{ "APIFrontend" ="Default Web Site/API" "AutodiscoverFrontend" ="Default Web Site/Autodiscover" @@ -124,13 +125,13 @@ begin { if ($RollbackType -eq "RestoreIISAppConfig") { $RollbackRestoreIISAppConfig = $true - } elseif ($SupportedRestrictTypes -contains $RollbackType.Replace("RestrictType", "")) { + } elseif ($SupportedRestrictTypes -contains $RollbackType) { $RollbackRestrictType = $true $RestrictType = $RollbackType.Replace("RestrictType", "") $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } else { - Write-Host "Please provide a valid value of RollbackType" -ForegroundColor Red + Write-Host "Please provide a valid value of RollbackType. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes)" -ForegroundColor Red exit } } @@ -140,24 +141,24 @@ begin { if(($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")){ if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation"){ - if ($SupportedRestrictTypes -contains $RestrictType) { + if ($SupportedVDirTypes -contains $RestrictType) { $ConfigureMitigationSelected = $true $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } else { - Write-Host "Please provide a valid value of RestrictType" -ForegroundColor Red + Write-Host "Please provide a valid value of RestrictType. Valid Values: $([string]::Join("/ " ,$SupportedVDirTypes)" -ForegroundColor Red exit } } if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { - if($SupportedRestrictTypes -contains $ValidateMitigation.Replace("RestrictType", "")) { + if($SupportedRestrictTypes -contains $ValidateMitigation) { $ValidateMitigationSelected = $true $RestrictType = $ValidateMitigation.Replace("RestrictType", "") $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } else { - Write-Host "Please provide a valid value of ValidateMitigation" -ForegroundColor Red + Write-Host "Please provide a valid value of ValidateMitigation. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes)" -ForegroundColor Red exit } } From 8e13a7ed69b972c5468349edfe83dbe736713c21 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 17:11:52 +0530 Subject: [PATCH 14/65] Move logger initialization to start of script --- .../ExchangeExtendedProtectionManagement.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 351c44ed9f..f8d84452bd 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -112,6 +112,12 @@ begin { "MAPI-nspiBackend" ="Exchange Back End/MAPI/nspi" } + $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` + -AppendDateTimeToFileName $false ` + -ErrorAction SilentlyContinue + + SetWriteHostAction ${Function:Write-HostLog} + $RollbackSelected = $false $RollbackRestrictType = $false $ConfigureEPSelected = $false @@ -200,12 +206,6 @@ begin { try { - $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` - -AppendDateTimeToFileName $false ` - -ErrorAction SilentlyContinue - - SetWriteHostAction ${Function:Write-HostLog} - if (-not((Confirm-ExchangeShell -Identity $env:COMPUTERNAME).ShellLoaded)) { Write-Warning "Failed to load the Exchange Management Shell. Start the script using the Exchange Management Shell." exit From 05942fbbaea63bf29246d90d785448ebb2a081c9 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 17:20:11 +0530 Subject: [PATCH 15/65] fixing issue in printing expected flag values --- .../ExchangeExtendedProtectionManagement.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index f8d84452bd..dcb96c79b1 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -137,7 +137,7 @@ begin { $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } else { - Write-Host "Please provide a valid value of RollbackType. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes)" -ForegroundColor Red + Write-Host "Please provide a valid value of RollbackType. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes))" -ForegroundColor Red exit } } @@ -152,7 +152,7 @@ begin { $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } else { - Write-Host "Please provide a valid value of RestrictType. Valid Values: $([string]::Join("/ " ,$SupportedVDirTypes)" -ForegroundColor Red + Write-Host "Please provide a valid value of RestrictType. Valid Values: $([string]::Join("/ " ,$SupportedVDirTypes))" -ForegroundColor Red exit } } @@ -164,7 +164,7 @@ begin { $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } else { - Write-Host "Please provide a valid value of ValidateMitigation. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes)" -ForegroundColor Red + Write-Host "Please provide a valid value of ValidateMitigation. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes))" -ForegroundColor Red exit } } From 695b3c1fb0607e2817f19714de0fe31a93bd19c8 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 10 Aug 2022 17:45:16 +0530 Subject: [PATCH 16/65] Validation fix for empty iprange list --- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index ee45c6fcf1..d47300e35c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -101,7 +101,7 @@ $ValidateMitigationScriptBlock = { $results.IsEPVerified = $true - if ($IpRangesForFiltering -ne $null) { + if ($null -ne $IpRangesForFiltering) { $localIPs = Get-LocalIpAddresses $localIPs | ForEach-Object { From 6199df0f6bd39d238534ddbca2f5146228f87975 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 10 Aug 2022 18:14:11 +0530 Subject: [PATCH 17/65] Fixed issue in validate: didn't compare ip.allow --- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index ee45c6fcf1..5bbb99e7ce 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -73,7 +73,7 @@ $ValidateMitigationScriptBlock = { $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection foreach ($IpFilteringRule in $IpFilteringRules) { - $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } + $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") -and $_.allowed -eq $IpFilteringRule.Allowed } if ($null -eq $ExistingIPSubnetRule) { if ($IpFilteringRule.Type -eq "Single IP") { $IpString = $IpFilteringRule.IP From f63fade0f164289bfd3dbd095788e408ec02f5d7 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 10 Aug 2022 19:31:57 +0530 Subject: [PATCH 18/65] code formatter --- .../Invoke-ConfigureMitigation.ps1 | 46 +++++++++---------- .../Invoke-RollbackIPFiltering.ps1 | 30 ++++++------ .../Invoke-ValidateMitigation.ps1 | 18 ++++---- .../Get-IPRangeAllowListFromFile.ps1 | 9 ++-- .../ExchangeExtendedProtectionManagement.ps1 | 29 ++++-------- 5 files changed, 61 insertions(+), 71 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 256bd3229f..3e5bd3cd9f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -32,15 +32,15 @@ $ConfigureMitigation = { $BackupPath ) - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection - $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - if ($null -eq $ExistingRules){ - $ExistingRules = @() - } - - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath - return $true + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + if ($null -eq $ExistingRules) { + $ExistingRules = @() + } + + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + return $true } function Get-LocalIpAddresses { @@ -85,7 +85,7 @@ $ConfigureMitigation = { [hashtable] $results ) - $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/','-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" $results.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath $Filter = 'system.webServer/security/ipSecurity' @@ -129,7 +129,7 @@ $ConfigureMitigation = { } try { - $results.IsTurnOffEPSuccessful = TurnOffEP -SiteVDirLocation $SiteVDirLocation + $results.IsTurnOffEPSuccessful = TurnOffEP -SiteVDirLocation $SiteVDirLocation if ($null -ne $IpRangesForFiltering) { $installResult = Install-WindowsFeature Web-IP-Security @@ -142,8 +142,8 @@ $ConfigureMitigation = { $localIPs = Get-LocalIpAddresses $results.IsGetLocalIPSuccessful = $true - ForEach ($localIP in $localIPs) { - if ($null -eq ($IpRangesForFiltering | Where-Object {$_.Type -eq "Single IP" -and $_.IP -eq $localIP -and $_.Allowed -eq $true})) { + foreach ($localIP in $localIPs) { + if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP -and $_.Allowed -eq $true })) { $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } } } @@ -199,7 +199,7 @@ function Invoke-ConfigureMitigation { $ShouldConfigureFilter = ($null -ne $ipRangeAllowListRules) Write-Verbose "Calling: $($MyInvocation.MyCommand)" - } process { + } process { $SiteVDirLocation = $Site if ($VDir -ne '') { $SiteVDirLocation += '/' + $VDir @@ -219,7 +219,7 @@ function Invoke-ConfigureMitigation { Write-Progress @progressParams $counter ++; - if($ipRangeAllowListRules -eq $null){ + if ($null -eq $ipRangeAllowListRules) { $ipRangeAllowListString = "null" } else { $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) @@ -230,7 +230,7 @@ function Invoke-ConfigureMitigation { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs if ($resultsInvoke.IsTurnOffEPSuccessful) { - Write-Host ("Successfully turned Off Extended Protection") + Write-Host ("Successfully turned Off Extended Protection") } else { Write-Host ("Script failed to Turn Off Extended protection with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -244,9 +244,9 @@ function Invoke-ConfigureMitigation { } Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) - + Write-Verbose ("Allow rules for the following IPs will be added: {0}" -f $ipRangeAllowListString) if ($resultsInvoke.IsWindowsFeatureInstalled) { - Write-Host ("Successfully installed windows feature - Web-IP-Security") + Write-Host ("Successfully installed windows feature - Web-IP-Security") } else { Write-Host ("Script failed to install windows feature - Web-IP-Security with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -255,7 +255,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsGetLocalIPSuccessful) { - Write-Host ("Successfully retrieved local IPs for the server") + Write-Host ("Successfully retrieved local IPs for the server") if ($null -ne $resultsInvoke.LocalIPs -and $resultsInvoke.LocalIPs.Length -gt 0) { Write-Verbose ("Local IPs detected for this server: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.LocalIPs)) } else { @@ -269,7 +269,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsBackUpSuccessful) { - Write-Host ("Successfully backed up IP filtering allow list") + Write-Host ("Successfully backed up IP filtering allow list") } else { Write-Host ("Script failed to backup IP filtering allow list with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -278,8 +278,8 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsCreateIPRulesSuccessful) { - Write-Host ("Successfully updated IP filtering allow list") - if ($resultsInvoke.IPsNotAdded.Length -gt 0) { + Write-Host ("Successfully updated IP filtering allow list") + if ($resultsInvoke.IPsNotAdded.Length -gt 0) { $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") Write-Warning ($line + "Check logs for further details.") Write-Verbose $line @@ -293,7 +293,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsSetDefaultRuleSuccessful) { - Write-Host ("Successfully set the default IP filtering rule to deny") + Write-Host ("Successfully set the default IP filtering rule to deny") } else { Write-Host ("Script failed to set the default IP filtering rule to deny with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 5a75532f99..0797794acd 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -33,15 +33,15 @@ $RollbackIPFiltering = { $BackupPath ) - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection - $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - if ($null -eq $ExistingRules){ - $ExistingRules = @() - } - - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath - return $true + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + if ($null -eq $ExistingRules) { + $ExistingRules = @() + } + + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + return $true } function Restore-OriginalIpFilteringRules { @@ -62,13 +62,13 @@ $RollbackIPFiltering = { } try { - $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/','-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName - if($results.RestorePath -eq $null){ + $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/', '-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName + if ($results.RestorePath -eq $null) { throw "Invalid operation. No backup file exisits at path $($env:WINDIR)\System32\inetsrv\config\" } $results.RestoreFileExists = $true - $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/','-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath $originalIpFilteringConfigurations = (Get-Content $results.RestorePath | Out-String | ConvertFrom-Json) @@ -122,7 +122,7 @@ function Invoke-RollbackIPFiltering { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs $Failed = $false - if($resultsInvoke.RestoreFileExists){ + if ($resultsInvoke.RestoreFileExists) { if ($resultsInvoke.BackupCurrentSuccessful) { Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" if ($resultsInvoke.RestoreSuccessful) { @@ -138,8 +138,8 @@ function Invoke-RollbackIPFiltering { $Failed = $true } } else { - Write-Host "No restore file exists on server $($Server.Name). Aborting rollback on the server $($Server.Name)." -ForegroundColor Red - $Failed = $true + Write-Host "No restore file exists on server $($Server.Name). Aborting rollback on the server $($Server.Name)." -ForegroundColor Red + $Failed = $true } if ($Failed) { diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 9080f7a65e..1977c5a908 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -13,15 +13,15 @@ $ValidateMitigationScriptBlock = { $IpRangesForFiltering = $Arguments.IpRangesForFiltering $results = @{ - IsEPVerified = $false - IsEPOff = $false + IsEPVerified = $false + IsEPOff = $false IsWindowsFeatureInstalled = $false - IsWindowsFeatureVerified = $false - AreIPRulesVerified = $false - IsDefaultFilterVerified = $false - IsDefaultFilterDeny = $false - RulesNotFound = @() - ErrorContext = $null + IsWindowsFeatureVerified = $false + AreIPRulesVerified = $false + IsDefaultFilterVerified = $false + IsDefaultFilterDeny = $false + RulesNotFound = @() + ErrorContext = $null } function Get-LocalIpAddresses { @@ -59,7 +59,7 @@ $ValidateMitigationScriptBlock = { [Parameter(Mandatory = $true)] [hashtable]$results ) - + $results.IsWindowsFeatureInstalled = (Get-WindowsFeature -Name "Web-IP-Security").InstallState -eq "Installed" $results.IsWindowsFeatureVerified = $true diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index 5844d841dd..89f2ef111b 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -31,7 +31,7 @@ function Get-IPRangeAllowListFromFile { } try { - $SubnetStrings = (Get-Content -Path $FilePath) | ? {$_.trim() -ne "" } + $SubnetStrings = (Get-Content -Path $FilePath) | Where-Object { $_.trim() -ne "" } } catch { Write-Host "Unable to read the content of file provided for IPRange. Inner Exception" -ForegroundColor Red Write-HostErrorInformation $_ @@ -97,22 +97,21 @@ function Get-IPRangeAllowListFromFile { $results.IsError = $true return } - if ($null -eq ($results.ipRangeAllowListRules | Where-Object {$_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString -and $_.Allowed -eq $true})) { + if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString -and $_.Allowed -eq $true })) { $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } } } else { - if ($null -eq ($results.ipRangeAllowListRules | Where-Object {$_.Type -eq "Single IP" -and $_.IP -eq $IpAddressString -and $_.Allowed -eq $true})) { + if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $IpAddressString -and $_.Allowed -eq $true })) { $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; Allowed=$true } } } } - if($results.ipRangeAllowListRules.count -gt 500){ + if ($results.ipRangeAllowListRules.count -gt 500) { Write-Host ("Too many IP filtering rules. Please reduce the specified entries by providing appropriate subnets." -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true return } - } catch { Write-Host ("Unable to create IP allow rules. Inner Exception") -ForegroundColor Red Write-HostErrorInformation $_ diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index dcb96c79b1..76e1c80cba 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -83,9 +83,9 @@ begin { . $PSScriptRoot\..\..\..\Shared\LoggerFunctions.ps1 . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 - + $SupportedVDirTypes = @('EWSBackend') - $SupportedRestrictTypes = $SupportedVDirTypes | ForEach-Object {"RestrictType$_"} + $SupportedRestrictTypes = $SupportedVDirTypes | ForEach-Object { "RestrictType$_" } $RestrictTypeToSiteVDirMap = @{ "APIFrontend" ="Default Web Site/API" "AutodiscoverFrontend" ="Default Web Site/Autodiscover" @@ -113,8 +113,8 @@ begin { } $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` - -AppendDateTimeToFileName $false ` - -ErrorAction SilentlyContinue + -AppendDateTimeToFileName $false ` + -ErrorAction SilentlyContinue SetWriteHostAction ${Function:Write-HostLog} @@ -142,11 +142,8 @@ begin { } } - - - if(($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")){ - - if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation"){ + if (($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")) { + if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { if ($SupportedVDirTypes -contains $RestrictType) { $ConfigureMitigationSelected = $true $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] @@ -158,7 +155,7 @@ begin { } if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { - if($SupportedRestrictTypes -contains $ValidateMitigation) { + if ($SupportedRestrictTypes -contains $ValidateMitigation) { $ValidateMitigationSelected = $true $RestrictType = $ValidateMitigation.Replace("RestrictType", "") $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] @@ -182,7 +179,6 @@ begin { $MitigationTypeSelected = 'OnlyEWSOffMitigation' } } - if ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) { $ConfigureEPSelected = $true @@ -205,7 +201,6 @@ begin { } try { - if (-not((Confirm-ExchangeShell -Identity $env:COMPUTERNAME).ShellLoaded)) { Write-Warning "Failed to load the Exchange Management Shell. Start the script using the Exchange Management Shell." exit @@ -263,7 +258,7 @@ begin { $ExchangeServers = $ExchangeServers | Where-Object { ($_.Name -notin $SkipExchangeServerNames) -and ($_.FQDN -notin $SkipExchangeServerNames) } } - if($ExchangeServers -eq $null){ + if ($null -eq $ExchangeServers) { Write-Host "No exchange servers to process. Please specify server filters correctly" exit } @@ -318,7 +313,6 @@ begin { $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS if ($null -ne $prerequisitesCheck) { - Write-Host "" # Remove the down servers from $ExchangeServers list. $downServerName = New-Object 'System.Collections.Generic.List[string]' @@ -362,7 +356,6 @@ begin { } if ($unsupportedServers.Count -gt 0) { - $serverInList = $null -ne ($ExchangeServers | Where-Object { $($_.Name -in $unsupportedServers) }) if ($serverInList) { @@ -509,16 +502,14 @@ begin { } else { Write-Host "No servers are online or no Exchange Servers Support Extended Protection." } - } - elseif ($ConfigureMitigationSelected) { + } elseif ($ConfigureMitigationSelected) { if ($MitigationTypeSelected -eq 'EWSOffAndIPMitigation') { # Apply rules Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } else { Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $null -Site $Site -VDir $VDir } - } - elseif ($ValidateMitigationSelected) { + } elseif ($ValidateMitigationSelected) { if ($MitigationTypeSelected -eq 'EWSOffAndIPMitigation') { # Validate mitigation Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir From 0922670c0f1b85ca34ab9a458c2bf0456e464019 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 10 Aug 2022 23:40:34 +0530 Subject: [PATCH 19/65] added a log statement in mitigation script --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 5 ++--- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 10 ++++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 3e5bd3cd9f..1f83a8759a 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -225,10 +225,10 @@ function Invoke-ConfigureMitigation { $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) } - $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs - Write-Host ("Setting Extended protection flag to None on Server {0}" -f $Server) + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs + Write-Host ("Setting Extended protection flag to None on Server {0}" -f $Server) if ($resultsInvoke.IsTurnOffEPSuccessful) { Write-Host ("Successfully turned Off Extended Protection") } else { @@ -244,7 +244,6 @@ function Invoke-ConfigureMitigation { } Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) - Write-Verbose ("Allow rules for the following IPs will be added: {0}" -f $ipRangeAllowListString) if ($resultsInvoke.IsWindowsFeatureInstalled) { Write-Host ("Successfully installed windows feature - Web-IP-Security") } else { diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 1977c5a908..5e1f229005 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -180,10 +180,16 @@ function Invoke-ValidateMitigation { Write-Progress @progressParams $counter ++; - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: list of length" -f $Server, $SiteVDirLocation, $ipRangeAllowListRules.Length.ToString()) - Write-Host ("Validating state of Extended protection flag on Server {0}" -f $Server) + if ($null -eq $ipRangeAllowListRules) { + $ipRangeAllowListString = "null" + } else { + $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) + } + + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs + Write-Host ("Validating state of Extended protection flag on Server {0}" -f $Server) if ($resultsInvoke.IsEPOff) { Write-Host ("Expected: The state of Extended protection flag is None") } elseif ($resultsInvoke.IsEPVerified) { From 3377c53dad2f2ed011b895b33cc6f562066a2112 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Fri, 12 Aug 2022 00:44:21 +0530 Subject: [PATCH 20/65] Resolved some comments --- .../Invoke-ConfigureMitigation.ps1 | 323 +++++++++--------- .../Invoke-RollbackIPFiltering.ps1 | 152 ++++----- .../Invoke-ValidateMitigation.ps1 | 250 +++++++------- .../DataCollection/Get-ExchangeServerIPs.ps1 | 14 +- .../Get-IPFilteringPrerequisitesCheck.ps1 | 35 -- .../Get-IPRangeAllowListFromFile.ps1 | 51 ++- .../ExchangeExtendedProtectionManagement.ps1 | 75 ++-- 7 files changed, 419 insertions(+), 481 deletions(-) delete mode 100644 Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 1f83a8759a..0d652cdfa6 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -4,201 +4,195 @@ . $PSScriptRoot\..\..\..\..\Shared\Invoke-ScriptBlockHandler.ps1 . $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 -# Steps: TurnOffEP -> Install windows feature -> GetLocalIP -> backup configuration -> CreateAllowRules -> SetDefaultToDeny -$ConfigureMitigation = { +function Invoke-ConfigureMitigation { + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] param( - [Object]$Arguments + [Parameter(Mandatory = $true)] + [string[]]$ExchangeServers, + [Parameter(Mandatory = $false)] + [object[]]$ipRangeAllowListRules, + [Parameter(Mandatory = $true)] + [string]$Site, + [Parameter(Mandatory = $true)] + [string]$VDir ) - $SiteVDirLocation = $Arguments.SiteVDirLocation - $IpRangesForFiltering = $Arguments.IpRangesForFiltering - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' - - $results = @{ - IsTurnOffEPSuccessful = $false - IsWindowsFeatureInstalled = $false - IsGetLocalIPSuccessful = $false - IsBackUpSuccessful = $false - IsCreateIPRulesSuccessful = $false - IsSetDefaultRuleSuccessful = $false - ErrorContext = $null - IPsNotAdded = @() - LocalIPs = @() - } - - function Backup-currentIpFilteringRules { - param( - $BackupPath - ) - - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection - $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - if ($null -eq $ExistingRules) { - $ExistingRules = @() - } - - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath - return $true - } + begin { + $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' + $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' - function Get-LocalIpAddresses { - $ips = @() - $interfaces = Get-NetIPAddress - foreach ($interface in $interfaces) { - if ($interface.AddressState -eq 'Preferred') { - $ips += $interface.IPAddress - } + $progressParams = @{ + Activity = "Turning Off EP and applying IP filtering Rules" + Status = [string]::Empty + PercentComplete = 0 } - return $ips - } + $ShouldConfigureFilter = ($null -ne $ipRangeAllowListRules) - # Set EP to None - function TurnOffEP { - param ( - [Parameter(Mandatory = $true)] - [string]$SiteVDirLocation - ) + Write-Verbose "Calling: $($MyInvocation.MyCommand)" - $Filter = 'system.webServer/security/authentication/windowsAuthentication/extendedProtection' - $IISPath = 'IIS:\' + $ConfigureMitigation = { + param( + [Object]$Arguments + ) + + $SiteVDirLocation = $Arguments.SiteVDirLocation + $IpRangesForFiltering = $Arguments.IpRangesForFiltering + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + + $results = @{ + IsTurnOffEPSuccessful = $false + IsWindowsFeatureInstalled = $false + IsGetLocalIPSuccessful = $false + IsBackUpSuccessful = $false + IsCreateIPRulesSuccessful = $false + IsSetDefaultRuleSuccessful = $false + ErrorContext = $null + IPsNotAdded = New-Object 'System.Collections.Generic.List[string]' + LocalIPs = New-Object 'System.Collections.Generic.List[string]' + } - $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking + function Backup-currentIpFilteringRules { + param( + $BackupPath + ) - if ($ExtendedProtection -ne "None") { - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" - } + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + if ($null -eq $ExistingRules) { + $ExistingRules = New-Object 'System.Collections.Generic.List[object]' + } - return $true - } + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + return $true + } - # Create ip allow list from user provided ip subnets - function CreateIPRangeAllowList { - param ( - [Parameter(Mandatory = $true)] - [string]$SiteVDirLocation, - [Parameter(Mandatory = $true)] - [object[]]$IpFilteringRules, - [Parameter(Mandatory = $true)] - [hashtable] $results - ) + function Get-LocalIpAddresses { + $ips = New-Object 'System.Collections.Generic.List[string]' + $interfaces = Get-NetIPAddress -ErrorAction Stop + foreach ($interface in $interfaces) { + if ($interface.AddressState -eq 'Preferred') { + $ips += $interface.IPAddress + } + } - $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" - $results.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath + return $ips + } - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' + # Set EP to None + function TurnOffEP { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation + ) - $RulesToBeAdded = @() - $IPsNotAdded = @() + $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + if ($ExtendedProtection -ne "None") { + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" + } - foreach ($IpFilteringRule in $IpFilteringRules) { - $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } + return $true + } - if ($null -eq $ExistingIPSubnetRule) { - if ($IpFilteringRule.Type -eq "Single IP") { - $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; } - } else { - $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; } - } - } else { - if ($ExistingIPSubnetRule.allowed -ne $IpFilteringRule.Allowed) { - if ($IpFilteringRule.Type -eq "Single IP") { - $IpString = $IpFilteringRule.IP + # Create ip allow list from user provided ip subnets + function CreateIPRangeAllowList { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $true)] + [object[]]$IpFilteringRules, + [Parameter(Mandatory = $true)] + [hashtable] $results + ) + + $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + $results.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath + + $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' + + foreach ($IpFilteringRule in $IpFilteringRules) { + $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } + + if ($null -eq $ExistingIPSubnetRule) { + if ($IpFilteringRule.Type -eq "Single IP") { + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; } + } else { + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; } + } } else { - $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + if ($ExistingIPSubnetRule.allowed -ne $IpFilteringRule.Allowed) { + if ($IpFilteringRule.Type -eq "Single IP") { + $IpString = $IpFilteringRule.IP + } else { + $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + } + + $results.IPsNotAdded += $IpString + } } - - $IPsNotAdded += $IpString } - } - } - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop - $results.IsCreateIPRulesSuccessful = $true + $results.IsCreateIPRulesSuccessful = $true - # Setting default to deny - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $false - $results.IsSetDefaultRuleSuccessful = $true - return $IPsNotAdded - } + # Setting default to deny + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $false + $results.IsSetDefaultRuleSuccessful = $true + } - try { - $results.IsTurnOffEPSuccessful = TurnOffEP -SiteVDirLocation $SiteVDirLocation + try { + $results.IsTurnOffEPSuccessful = TurnOffEP -SiteVDirLocation $SiteVDirLocation + + if ($null -ne $IpRangesForFiltering) { + try { + $baseError = "Installation of IP and Domain filtering Module failed." + $InstallResult = Install-WindowsFeature Web1-IP-Security -ErrorAction Stop + if (-not $InstallResult.Success) { + throw $baseError + } + } catch { + throw "$baseError Inner exception: $_" + } - if ($null -ne $IpRangesForFiltering) { - $installResult = Install-WindowsFeature Web-IP-Security - if (-not $installResult) { - throw ("Unable to install Windows feature Web-IP-Security which is required to add IP filtering rules to IIS.") - } + $results.IsWindowsFeatureInstalled = $true - $results.IsWindowsFeatureInstalled = $true + $localIPs = Get-LocalIpAddresses + $results.IsGetLocalIPSuccessful = $true - $localIPs = Get-LocalIpAddresses - $results.IsGetLocalIPSuccessful = $true + foreach ($localIP in $localIPs) { + if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP })) { + $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } + } + } - foreach ($localIP in $localIPs) { - if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP -and $_.Allowed -eq $true })) { - $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } + $results.LocalIPs = $localIPs + CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results } + } catch { + $results.ErrorContext = $_ } - $results.LocalIPs = $localIPs - $results.IPsNotAdded = CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results + return $results } - } catch { - $results.ErrorContext = $_ - } - - return $results -} -function GetCommaSaperatedString { - param( - [Parameter(Mandatory = $true)] - [object[]]$list - ) + function GetCommaSaperatedString { + param( + [Parameter(Mandatory = $true)] + [object[]]$list + ) - $string = "" - foreach ($element in $list) { - $string += ($element.ToString() + ", ") - } - - return $string.Trim(", ") -} - -function Invoke-ConfigureMitigation { - [OutputType([System.Collections.Hashtable])] - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [string[]]$ExchangeServers, - [Parameter(Mandatory = $false)] - [object[]]$ipRangeAllowListRules, - [Parameter(Mandatory = $true)] - [string]$Site, - [Parameter(Mandatory = $true)] - [string]$VDir - ) - - begin { - $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' - $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' + $string = "" + foreach ($element in $list) { + $string += ($element.ToString() + ", ") + } - $progressParams = @{ - Activity = "Turning Off EP and applying IP filtering Rules" - Status = [string]::Empty - PercentComplete = 0 + return $string.Trim(", ") } - - $ShouldConfigureFilter = ($null -ne $ipRangeAllowListRules) - - Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { $SiteVDirLocation = $Site if ($VDir -ne '') { @@ -212,6 +206,13 @@ function Invoke-ConfigureMitigation { $counter = 0 $totalCount = $ExchangeServers.Count + + if ($null -eq $ipRangeAllowListRules) { + $ipRangeAllowListString = "null" + } else { + $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) + } + foreach ($Server in $ExchangeServers) { $baseStatus = "Processing: $Server -" $progressParams.PercentComplete = ($counter / $totalCount * 100) @@ -219,12 +220,6 @@ function Invoke-ConfigureMitigation { Write-Progress @progressParams $counter ++; - if ($null -eq $ipRangeAllowListRules) { - $ipRangeAllowListString = "null" - } else { - $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) - } - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 0797794acd..736e05e3d0 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -4,82 +4,6 @@ . $PSScriptRoot\..\..\..\..\Shared\Invoke-ScriptBlockHandler.ps1 . $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 -$RollbackIPFiltering = { - param( - [Object]$Arguments - ) - - $Site = $Arguments.Site - $VDir = $Arguments.VDir - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' - - $SiteVDirLocation = $Site - if ($VDir -ne '') { - $SiteVDirLocation += '/' + $VDir - } - - $results = @{ - RestoreFileExists = $false - BackUpPath = $null - BackupCurrentSuccessful = $false - RestorePath = $null - RestoreSuccessful = $false - ErrorContext = $null - } - - function Backup-currentIpFilteringRules { - param( - $BackupPath - ) - - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection - $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" - if ($null -eq $ExistingRules) { - $ExistingRules = @() - } - - $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath - return $true - } - - function Restore-OriginalIpFilteringRules { - param( - $OriginalIpFilteringRules, - $DefaultForUnspecifiedIPs - ) - - Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop - $RulesToBeAdded = @() - foreach ($IpFilteringRule in $OriginalIpFilteringRules) { - $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } - } - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop - - return $true - } - - try { - $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/', '-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName - if ($results.RestorePath -eq $null) { - throw "Invalid operation. No backup file exisits at path $($env:WINDIR)\System32\inetsrv\config\" - } - $results.RestoreFileExists = $true - - $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" - $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath - - $originalIpFilteringConfigurations = (Get-Content $results.RestorePath | Out-String | ConvertFrom-Json) - $results.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) - } catch { - $results.ErrorContext = $_ - } - - return $results -} - function Invoke-RollbackIPFiltering { [OutputType([System.Collections.Hashtable])] [CmdletBinding()] @@ -101,6 +25,82 @@ function Invoke-RollbackIPFiltering { PercentComplete = 0 } + $RollbackIPFiltering = { + param( + [Object]$Arguments + ) + + $Site = $Arguments.Site + $VDir = $Arguments.VDir + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + + $SiteVDirLocation = $Site + if ($VDir -ne '') { + $SiteVDirLocation += '/' + $VDir + } + + $results = @{ + RestoreFileExists = $false + BackUpPath = $null + BackupCurrentSuccessful = $false + RestorePath = $null + RestoreSuccessful = $false + ErrorContext = $null + } + + function Backup-currentIpFilteringRules { + param( + $BackupPath + ) + + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" + if ($null -eq $ExistingRules) { + $ExistingRules = New-Object 'System.Collections.Generic.List[object]' + } + + $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + return $true + } + + function Restore-OriginalIpFilteringRules { + param( + $OriginalIpFilteringRules, + $DefaultForUnspecifiedIPs + ) + + Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop + $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' + foreach ($IpFilteringRule in $OriginalIpFilteringRules) { + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } + } + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop + + return $true + } + + try { + $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/', '-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName + if ($null -eq $results.RestorePath) { + throw "Invalid operation. No backup file exisits at path $($env:WINDIR)\System32\inetsrv\config\" + } + $results.RestoreFileExists = $true + + $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath + + $originalIpFilteringConfigurations = (Get-Content $results.RestorePath | Out-String | ConvertFrom-Json) + $results.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) + } catch { + $results.ErrorContext = $_ + } + + return $results + } + Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { $scriptblockArgs = [PSCustomObject]@{ diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 5e1f229005..7d6741b3d5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -4,162 +4,162 @@ . $PSScriptRoot\..\..\..\..\Shared\Invoke-ScriptBlockHandler.ps1 . $PSScriptRoot\..\..\..\..\Shared\Write-ErrorInformation.ps1 -$ValidateMitigationScriptBlock = { +function Invoke-ValidateMitigation { + [OutputType([System.Collections.Hashtable])] + [CmdletBinding()] param( - [Object]$Arguments + [Parameter(Mandatory = $true)] + [string[]]$ExchangeServers, + [Parameter(Mandatory = $false)] + $ipRangeAllowListRules, + [Parameter(Mandatory = $true)] + [string]$Site, + [Parameter(Mandatory = $true)] + [string]$VDir ) - $SiteVDirLocation = $Arguments.SiteVDirLocation - $IpRangesForFiltering = $Arguments.IpRangesForFiltering - - $results = @{ - IsEPVerified = $false - IsEPOff = $false - IsWindowsFeatureInstalled = $false - IsWindowsFeatureVerified = $false - AreIPRulesVerified = $false - IsDefaultFilterVerified = $false - IsDefaultFilterDeny = $false - RulesNotFound = @() - ErrorContext = $null - } + begin { + $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' + $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' - function Get-LocalIpAddresses { - $ips = @() - $interfaces = Get-NetIPAddress - foreach ($interface in $interfaces) { - if ($interface.AddressState -eq 'Preferred') { - $ips += $interface.IPAddress - } + $UnMitigatedServersEP = New-Object 'System.Collections.Generic.List[string]' + $UnMitigatedServersFilter = New-Object 'System.Collections.Generic.List[string]' + + $progressParams = @{ + Activity = "Verifying Mitigations" + Status = [string]::Empty + PercentComplete = 0 } - return $ips - } + $ShouldVerifyFilter = ($null -ne $ipRangeAllowListRules) - # Set EP to None - function GetEPState { - param ( - [Parameter(Mandatory = $true)] - [string]$SiteVDirLocation - ) + Write-Verbose "Calling: $($MyInvocation.MyCommand)" - $Filter = 'system.webServer/security/authentication/windowsAuthentication/extendedProtection' + function GetCommaSaperatedString { + param( + $list + ) - $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking - return $ExtendedProtection - } + $string = "" + foreach ($element in $list) { + $string += ($element.ToString() + ", ") + } - # Create ip allow list from user provided ip subnets - function VerifyIPRangeAllowList { - param ( - [Parameter(Mandatory = $true)] - [string]$SiteVDirLocation, - [Parameter(Mandatory = $true)] - [object[]]$IpFilteringRules, - [Parameter(Mandatory = $true)] - [hashtable]$results - ) - - $results.IsWindowsFeatureInstalled = (Get-WindowsFeature -Name "Web-IP-Security").InstallState -eq "Installed" - $results.IsWindowsFeatureVerified = $true - - if (-not $results.IsWindowsFeatureInstalled) { - return + return $string.Trim(", ") } - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' - - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $ValidateMitigationScriptBlock = { + param( + [Object]$Arguments + ) + + $SiteVDirLocation = $Arguments.SiteVDirLocation + $IpRangesForFiltering = $Arguments.IpRangesForFiltering + + $results = @{ + IsEPVerified = $false + IsEPOff = $false + IsWindowsFeatureInstalled = $false + IsWindowsFeatureVerified = $false + AreIPRulesVerified = $false + IsDefaultFilterVerified = $false + IsDefaultFilterDeny = $false + RulesNotFound = New-Object 'System.Collections.Generic.List[string]' + ErrorContext = $null + } - foreach ($IpFilteringRule in $IpFilteringRules) { - $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") -and $_.allowed -eq $IpFilteringRule.Allowed } - if ($null -eq $ExistingIPSubnetRule) { - if ($IpFilteringRule.Type -eq "Single IP") { - $IpString = $IpFilteringRule.IP - } else { - $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + function Get-LocalIpAddresses { + $ips = New-Object 'System.Collections.Generic.List[string]' + $interfaces = Get-NetIPAddress + foreach ($interface in $interfaces) { + if ($interface.AddressState -eq 'Preferred') { + $ips += $interface.IPAddress + } } - $results.RulesNotFound += $IpString + + return $ips } - } - $results.AreIPRulesVerified = $true + # Set EP to None + function GetEPState { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation + ) - $results.IsDefaultFilterDeny = -not ((Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted").Value) - $results.IsDefaultFilterVerified = $true - } + $Filter = 'system.webServer/security/authentication/windowsAuthentication/extendedProtection' - try { - $EPState = GetEPState -SiteVDirLocation $SiteVDirLocation + $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking + return $ExtendedProtection + } - if ($EPState -eq "None") { - $results.IsEPOff = $true - } else { - $results.IsEPOff = $false - } + # Create ip allow list from user provided ip subnets + function VerifyIPRangeAllowList { + param ( + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $true)] + [object[]]$IpFilteringRules, + [Parameter(Mandatory = $true)] + [hashtable]$results + ) + + $results.IsWindowsFeatureInstalled = (Get-WindowsFeature -Name "Web-IP-Security").InstallState -eq "Installed" + $results.IsWindowsFeatureVerified = $true + + if (-not $results.IsWindowsFeatureInstalled) { + return + } - $results.IsEPVerified = $true + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + + foreach ($IpFilteringRule in $IpFilteringRules) { + $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") -and $_.allowed -eq $IpFilteringRule.Allowed } + if ($null -eq $ExistingIPSubnetRule) { + if ($IpFilteringRule.Type -eq "Single IP") { + $IpString = $IpFilteringRule.IP + } else { + $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) + } + $results.RulesNotFound += $IpString + } + } - if ($null -ne $IpRangesForFiltering) { - $localIPs = Get-LocalIpAddresses + $results.AreIPRulesVerified = $true - $localIPs | ForEach-Object { - $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } + $results.IsDefaultFilterDeny = -not ((Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted").Value) + $results.IsDefaultFilterVerified = $true } - VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results - } - } catch { - $results.ErrorContext = $_ - } - - return $results -} - -function GetCommaSaperatedString { - param( - $list - ) + try { + $EPState = GetEPState -SiteVDirLocation $SiteVDirLocation - $string = "" - foreach ($element in $list) { - $string += ($element.ToString() + ", ") - } + if ($EPState -eq "None") { + $results.IsEPOff = $true + } else { + $results.IsEPOff = $false + } - return $string.Trim(", ") -} + $results.IsEPVerified = $true -function Invoke-ValidateMitigation { - [OutputType([System.Collections.Hashtable])] - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [string[]]$ExchangeServers, - [Parameter(Mandatory = $false)] - $ipRangeAllowListRules, - [Parameter(Mandatory = $true)] - [string]$Site, - [Parameter(Mandatory = $true)] - [string]$VDir - ) + if ($null -ne $IpRangesForFiltering) { + $localIPs = Get-LocalIpAddresses - begin { - $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' - $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' + $localIPs | ForEach-Object { + $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } + } - $UnMitigatedServersEP = New-Object 'System.Collections.Generic.List[string]' - $UnMitigatedServersFilter = New-Object 'System.Collections.Generic.List[string]' + VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results + } + } catch { + $results.ErrorContext = $_ + } - $progressParams = @{ - Activity = "Verifying Mitigations" - Status = [string]::Empty - PercentComplete = 0 + return $results } - - $ShouldVerifyFilter = ($null -ne $ipRangeAllowListRules) - - Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { $SiteVDirLocation = $Site if ($VDir -ne '') { diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 index 9278d46f6f..bce53d36c6 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -13,8 +13,8 @@ function Get-ExchangeServerIPs { ) begin { - $IPs = @() - $FailedServers = @() + $IPs = New-Object 'System.Collections.Generic.List[string]' + $FailedServers = New-Object 'System.Collections.Generic.List[string]' $progressParams = @{ Activity = "Getting List of IPs in use by Exchange Servers" @@ -26,11 +26,11 @@ function Get-ExchangeServerIPs { } process { try { - $ExchangeServers = Get-ExchangeServer + $ExchangeServers = Get-ExchangeServer -ErrorAction Stop } catch { Write-Host ("Unable to run Get-ExchangeServer due to: Inner Exception") -ForegroundColor Red Write-HostErrorInformation $_ - exit + return } $counter = 0 @@ -54,13 +54,17 @@ function Get-ExchangeServerIPs { $IPs += $address.Address } } - } else { + } + + if ($IPs.Length -eq 0) { $FailedServers += $Server.Name Write-Verbose "Ip of $($Server.Name) cannot be found and will not be added to ip allow list." -ForegroundColor Red } $counter++ } + + Write-Progress @progressParams -Completed } end { if ($FailedServers -gt 0) { diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 deleted file mode 100644 index 59f44f7a8a..0000000000 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPFilteringPrerequisitesCheck.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# This function is used to collect the required information needed to determine if a server is ready for IP Filtering mitigation -function Get-IPFilteringPrerequisitesCheck { - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [object[]]$ExchangeServers - ) - begin { - $results = New-Object 'System.Collections.Generic.List[object]' - Write-Verbose "Calling: $($MyInvocation.MyCommand)" - } process { - foreach ($server in $ExchangeServers) { - Write-Verbose ("Performing prereq checks on server: {0}" -f $server.ToString()) - - $computerResult = Invoke-ScriptBlockHandler -ComputerName $server.ToString() -ScriptBlock { return $env:COMPUTERNAME } - $serverConnected = $null -ne $computerResult - - if ($serverConnected) { - Write-Verbose ("Server {0} appears to up and reachable" -f $server.ToString()) - } else { - Write-Verbose ("Server {0} doesn't appear to be online." -f $server.ToString()) - } - - $results.Add([PSCustomObject]@{ - ComputerName = $server.ToString() - ServerOnline = $serverConnected - }) - } - } end { - return $results - } -} diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index 89f2ef111b..48ee6a55b0 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -14,33 +14,23 @@ function Get-IPRangeAllowListFromFile { begin { $results = @{ - ipRangeAllowListRules = @() - IsError = $false + ipRangeAllowListRules = New-Object 'System.Collections.Generic.List[object]' + IsError = $true } Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { - $FilePath = $FilePath.Trim('"', "'") - $IsPathValid = Test-Path -Path $FilePath - - if ($IsPathValid -eq $false) { - Write-Host "Input file name for provided for IPRange isn't valid. Rexecute the command with correct path for IPRange parameter." -ForegroundColor Red - $results.IsError = $true - return - } - try { - $SubnetStrings = (Get-Content -Path $FilePath) | Where-Object { $_.trim() -ne "" } + $SubnetStrings = (Get-Content -Path $FilePath -ErrorAction Stop) | Where-Object { $_.trim() -ne "" } } catch { Write-Host "Unable to read the content of file provided for IPRange. Inner Exception" -ForegroundColor Red Write-HostErrorInformation $_ - $results.IsError = $true - return + return $results } if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { - $SubnetStrings = @() + $SubnetStrings = New-Object 'System.Collections.Generic.List[string]' Write-Warning "The provided file is empty." $params = @{ Message = "Display Warning about using an empty ip file for ip filtering" @@ -71,55 +61,54 @@ function Get-IPRangeAllowListFromFile { $SubnetMaskString = $SubnetString.Split("/")[1] # Check the type of IP address (IPv4/IPv6) - $IsIPv6 = $false $IpAddress = $IpAddressString -as [IPAddress] - if ($null -eq $IpAddress -or ($IpAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetwork -and $IpAddress.AddressFamily -ne [System.Net.Sockets.AddressFamily]::InterNetworkV6)) { + if ($null -eq $IpAddress -or $null -eq $IpAddress.AddressFamily) { # Invalid IP address found Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid IP address detected: {0}." -f $IpAddressString) -ForegroundColor Red - $results.IsError = $true - return - } elseif ($IpAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6) { - $IsIPv6 = $true; + return $results } - $IsSubnetMaskPresent = [Bool]$SubnetMaskString + $IsIPv6 = $IpAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6 - if ($IsSubnetMaskPresent) { + if ($SubnetMaskString) { # Check if the subnet value is valid (IPv4 <= 32, IPv6 <= 128 or empty) $SubnetMask = $SubnetMaskString -as [int] if ($null -eq $SubnetMask) { Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid Subnet Mask found: Unable to parse Subnet Mask {0}. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true - return + return $results } elseif (($SubnetMask -gt 32 -and -not $IsIPv6) -or $SubnetMask -gt 128 -or $SubnetMask -lt 0) { Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true - return + return $results } - if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString -and $_.Allowed -eq $true })) { + if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString })) { $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } + } else { + Write-Verbose ("Not adding $IpAddressString/$SubnetMaskString to the list as it is a duplicate entry in the file provided.") } } else { - if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $IpAddressString -and $_.Allowed -eq $true })) { + if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $IpAddressString })) { $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; Allowed=$true } + } else { + Write-Verbose ("Not adding $IpAddressString to the list as it is a duplicate entry in the file provided.") } } } if ($results.ipRangeAllowListRules.count -gt 500) { Write-Host ("Too many IP filtering rules. Please reduce the specified entries by providing appropriate subnets." -f $SubnetMaskString) -ForegroundColor Red - $results.IsError = $true - return + return $results } } catch { Write-Host ("Unable to create IP allow rules. Inner Exception") -ForegroundColor Red Write-HostErrorInformation $_ - $results.IsError = $true - return + return $results } } end { + $results.IsError = $false return $results } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 76e1c80cba..57db91b8b8 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -25,6 +25,7 @@ This will set the applicationHost.config file back to the original state prior to changes made with this script. #> [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param( [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enter the list of server names on which the script should execute on")] [Parameter (Mandatory = $false, ValueFromPipeline, ParameterSetName = 'ValidateMitigation', HelpMessage = "Enter the list of server names on which the script should execute on")] @@ -48,19 +49,25 @@ param( [switch]$FindExchangeServerIPAddresses, [Parameter (Mandatory = $false, ParameterSetName = 'GetExchangeIPs', HelpMessage = "Using this parameter will allow you to specify the path to the output file.")] - [string]$OutputFilePath, + [ValidateScript({ + (Test-Path -Path $_ -IsValid) -and (Test-Path -Path (Split-Path -Parent $_)) + })] + [string]$OutputFilePath = [System.IO.Path]::Combine((Get-Location).Path, "IPList.txt"), [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to apply IP filters.")] [Parameter (Mandatory = $false, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to validate IP filters.")] [string]$IPRange, [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir on which you want to configure mitigation.")] + [ValidateSet('EWSBackend')] [string]$RestrictType, [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this switch will allow you to validate if the mitigations have been applied correctly.")] + [ValidateSet('RestrictTypeEWSBackend')] [string]$ValidateMitigation, [Parameter (Mandatory = $true, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] + [ValidateSet('RestrictTypeEWSBackend', 'RestoreIISAppConfig')] [string]$RollbackType ) @@ -72,7 +79,6 @@ begin { . $PSScriptRoot\ConfigurationAction\Invoke-RollbackIPFiltering.ps1 . $PSScriptRoot\ConfigurationAction\Invoke-ConfigureExtendedProtection.ps1 . $PSScriptRoot\ConfigurationAction\Invoke-RollbackExtendedProtection.ps1 - . $PSScriptRoot\DataCollection\Get-IPFilteringPrerequisitesCheck.ps1 . $PSScriptRoot\DataCollection\Get-ExchangeServerIPs.ps1 . $PSScriptRoot\DataCollection\Get-IPRangeAllowListFromFile.ps1 . $PSScriptRoot\DataCollection\Get-ExtendedProtectionPrerequisitesCheck.ps1 @@ -84,8 +90,6 @@ begin { . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 - $SupportedVDirTypes = @('EWSBackend') - $SupportedRestrictTypes = $SupportedVDirTypes | ForEach-Object { "RestrictType$_" } $RestrictTypeToSiteVDirMap = @{ "APIFrontend" ="Default Web Site/API" "AutodiscoverFrontend" ="Default Web Site/Autodiscover" @@ -131,52 +135,39 @@ begin { if ($RollbackType -eq "RestoreIISAppConfig") { $RollbackRestoreIISAppConfig = $true - } elseif ($SupportedRestrictTypes -contains $RollbackType) { + } else { $RollbackRestrictType = $true $RestrictType = $RollbackType.Replace("RestrictType", "") $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] - } else { - Write-Host "Please provide a valid value of RollbackType. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes))" -ForegroundColor Red - exit } } if (($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")) { if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { - if ($SupportedVDirTypes -contains $RestrictType) { - $ConfigureMitigationSelected = $true - $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] - $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] - } else { - Write-Host "Please provide a valid value of RestrictType. Valid Values: $([string]::Join("/ " ,$SupportedVDirTypes))" -ForegroundColor Red - exit - } + $ConfigureMitigationSelected = $true + $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] + $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { - if ($SupportedRestrictTypes -contains $ValidateMitigation) { - $ValidateMitigationSelected = $true - $RestrictType = $ValidateMitigation.Replace("RestrictType", "") - $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] - $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] - } else { - Write-Host "Please provide a valid value of ValidateMitigation. Valid Values: $([string]::Join("/ " ,$SupportedRestrictTypes))" -ForegroundColor Red - exit - } + $ValidateMitigationSelected = $true + $RestrictType = $ValidateMitigation.Replace("RestrictType", "") + $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] + $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } if ($PSBoundParameters.ContainsKey("IPRange")) { - $MitigationTypeSelected = 'EWSOffAndIPMitigation' + $MitigationTypeSelected = 'EPOffAndIPMitigation' # Get list of IPs in object form from the file specified $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRange if ($ipResults.IsError) { - exit + return } $ipRangeAllowListRules = $ipResults.ipRangeAllowListRules } else { - $MitigationTypeSelected = 'OnlyEWSOffMitigation' + $MitigationTypeSelected = 'OnlyEPOffMitigation' } } @@ -215,10 +206,6 @@ begin { } if ($FindExchangeServerIPAddresses) { - if ([String]::IsNullOrEmpty($OutputFilePath)) { - $OutputFilePath = [System.IO.Path]::Combine((Get-Location).Path, "IPList.txt") - } - Get-ExchangeServerIPs -OutputFilePath $OutputFilePath return } @@ -260,7 +247,16 @@ begin { if ($null -eq $ExchangeServers) { Write-Host "No exchange servers to process. Please specify server filters correctly" - exit + return + } + + if ($ValidateMitigationSelected) { + if ($MitigationTypeSelected -eq 'EPOffAndIPMitigation') { + # Validate mitigation + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + } else { + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -Site $Site -VDir $VDir + } } if ($ShowExtendedProtection) { @@ -306,10 +302,6 @@ begin { Write-Host "IP Restrictions will only be applied on the servers which pass the Extended Protection Prerequisite Check" } - if ($ValidateMitigation) { - Write-Host "IP Restrictions will only be validated on the servers which pass the Extended Protection Prerequisite Check" - } - $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS if ($null -ne $prerequisitesCheck) { @@ -503,19 +495,12 @@ begin { Write-Host "No servers are online or no Exchange Servers Support Extended Protection." } } elseif ($ConfigureMitigationSelected) { - if ($MitigationTypeSelected -eq 'EWSOffAndIPMitigation') { + if ($MitigationTypeSelected -eq 'EPOffAndIPMitigation') { # Apply rules Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } else { Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $null -Site $Site -VDir $VDir } - } elseif ($ValidateMitigationSelected) { - if ($MitigationTypeSelected -eq 'EWSOffAndIPMitigation') { - # Validate mitigation - Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir - } else { - Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $null -Site $Site -VDir $VDir - } } } elseif ($RollbackSelected) { Write-Host "Prerequisite check will be skipped due to Rollback" From 2a1956c1cd03e3635d079f81839958cbbee9ce47 Mon Sep 17 00:00:00 2001 From: akshar Date: Fri, 12 Aug 2022 02:12:58 +0530 Subject: [PATCH 21/65] ConfigureMitigation Whatif and logging changes --- .../Invoke-ConfigureMitigation.ps1 | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 0d652cdfa6..210c40ef80 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -28,8 +28,6 @@ function Invoke-ConfigureMitigation { PercentComplete = 0 } - $ShouldConfigureFilter = ($null -ne $ipRangeAllowListRules) - Write-Verbose "Calling: $($MyInvocation.MyCommand)" $ConfigureMitigation = { @@ -39,6 +37,7 @@ function Invoke-ConfigureMitigation { $SiteVDirLocation = $Arguments.SiteVDirLocation $IpRangesForFiltering = $Arguments.IpRangesForFiltering + $WhatIf = $Arguments.PassedWhatIf $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection @@ -66,7 +65,10 @@ function Invoke-ConfigureMitigation { } $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + if(-not $WhatIf){ + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + } + return $true } @@ -90,9 +92,8 @@ function Invoke-ConfigureMitigation { ) $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking - if ($ExtendedProtection -ne "None") { - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" -WhatIf:$WhatIf } return $true @@ -136,12 +137,11 @@ function Invoke-ConfigureMitigation { } } - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop - + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf $results.IsCreateIPRulesSuccessful = $true # Setting default to deny - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $false + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $false -WhatIf:$WhatIf $results.IsSetDefaultRuleSuccessful = $true } @@ -151,7 +151,7 @@ function Invoke-ConfigureMitigation { if ($null -ne $IpRangesForFiltering) { try { $baseError = "Installation of IP and Domain filtering Module failed." - $InstallResult = Install-WindowsFeature Web1-IP-Security -ErrorAction Stop + $InstallResult = Install-WindowsFeature Web1-IP-Security -ErrorAction Stop -WhatIf:$WhatIf if (-not $InstallResult.Success) { throw $baseError } @@ -202,6 +202,7 @@ function Invoke-ConfigureMitigation { $scriptblockArgs = [PSCustomObject]@{ SiteVDirLocation = $SiteVDirLocation IpRangesForFiltering = $ipRangeAllowListRules + PassedWhatIf = $WhatIfPreference } $counter = 0 @@ -223,47 +224,46 @@ function Invoke-ConfigureMitigation { Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs - Write-Host ("Setting Extended protection flag to None on Server {0}" -f $Server) + #Write-Host ("Setting Extended protection flag to None on Server {0}" -f $Server) if ($resultsInvoke.IsTurnOffEPSuccessful) { - Write-Host ("Successfully turned Off Extended Protection") + Write-Verbose ("Successfully turned Off Extended Protection on server {0}" -f $Server) } else { - Write-Host ("Script failed to Turn Off Extended protection with the Inner Exception:") -ForegroundColor Red + Write-Host ("Script failed to Turn Off Extended protection on server {0} with the Inner Exception:" -f $Server) -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $FailedServersEP += $Server $FailedServersFilter += $Server continue } - if (-not $ShouldConfigureFilter) { + if ($null -eq $ipRangeAllowListRules) { continue } - Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) if ($resultsInvoke.IsWindowsFeatureInstalled) { - Write-Host ("Successfully installed windows feature - Web-IP-Security") + Write-Verbose ("Successfully installed windows feature - Web-IP-Security on server {0}" -f $Server) } else { - Write-Host ("Script failed to install windows feature - Web-IP-Security with the Inner Exception:") -ForegroundColor Red + Write-Host ("Script failed to install windows feature - Web-IP-Security on server {0} with the Inner Exception:" -f $Server) -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $FailedServersFilter += $Server continue } if ($resultsInvoke.IsGetLocalIPSuccessful) { - Write-Host ("Successfully retrieved local IPs for the server") + Write-Verbose ("Successfully retrieved local IPs for the server") if ($null -ne $resultsInvoke.LocalIPs -and $resultsInvoke.LocalIPs.Length -gt 0) { Write-Verbose ("Local IPs detected for this server: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.LocalIPs)) } else { Write-Verbose ("No Local IPs detected for this server") } } else { - Write-Host ("Script failed to retrieve local IPs for the server with the Inner Exception:") -ForegroundColor Red + Write-Host ("Script failed to retrieve local IPs for server {0} with the Inner Exception:" -f $Server) -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $FailedServersFilter += $Server continue } if ($resultsInvoke.IsBackUpSuccessful) { - Write-Host ("Successfully backed up IP filtering allow list") + Write-Verbose ("Successfully backed up IP filtering allow list") } else { Write-Host ("Script failed to backup IP filtering allow list with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext @@ -272,7 +272,7 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsCreateIPRulesSuccessful) { - Write-Host ("Successfully updated IP filtering allow list") + Write-Verbose ("Successfully updated IP filtering allow list") if ($resultsInvoke.IPsNotAdded.Length -gt 0) { $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") Write-Warning ($line + "Check logs for further details.") @@ -287,13 +287,15 @@ function Invoke-ConfigureMitigation { } if ($resultsInvoke.IsSetDefaultRuleSuccessful) { - Write-Host ("Successfully set the default IP filtering rule to deny") + Write-Verbose ("Successfully set the default IP filtering rule to deny") } else { Write-Host ("Script failed to set the default IP filtering rule to deny with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $FailedServersFilter += $Server continue } + + Write-Host ("Enabled ip filtering rules on server {0}" -f $Server) } } end { if ($FailedServersEP.Length -gt 0) { From ac3baa94f191f8236954cedbbf1aa1d31e8db26e Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Fri, 12 Aug 2022 03:08:16 +0530 Subject: [PATCH 22/65] Merging Mitigation into Enable EP feature --- .../Invoke-ConfigureMitigation.ps1 | 80 ++++++------------ .../Get-IPRangeAllowListFromFile.ps1 | 17 +--- .../ExchangeExtendedProtectionManagement.ps1 | 82 +++++++++---------- 3 files changed, 63 insertions(+), 116 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 210c40ef80..2b424207d6 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -10,7 +10,7 @@ function Invoke-ConfigureMitigation { param( [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, - [Parameter(Mandatory = $false)] + [Parameter(Mandatory = $true)] [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] [string]$Site, @@ -19,11 +19,10 @@ function Invoke-ConfigureMitigation { ) begin { - $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' $progressParams = @{ - Activity = "Turning Off EP and applying IP filtering Rules" + Activity = "Applying IP filtering Rules" Status = [string]::Empty PercentComplete = 0 } @@ -43,7 +42,6 @@ function Invoke-ConfigureMitigation { $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection $results = @{ - IsTurnOffEPSuccessful = $false IsWindowsFeatureInstalled = $false IsGetLocalIPSuccessful = $false IsBackUpSuccessful = $false @@ -65,7 +63,7 @@ function Invoke-ConfigureMitigation { } $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - if(-not $WhatIf){ + if (-not $WhatIf) { $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath } @@ -84,21 +82,6 @@ function Invoke-ConfigureMitigation { return $ips } - # Set EP to None - function TurnOffEP { - param ( - [Parameter(Mandatory = $true)] - [string]$SiteVDirLocation - ) - - $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking - if ($ExtendedProtection -ne "None") { - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "None" -WhatIf:$WhatIf - } - - return $true - } - # Create ip allow list from user provided ip subnets function CreateIPRangeAllowList { param ( @@ -146,33 +129,29 @@ function Invoke-ConfigureMitigation { } try { - $results.IsTurnOffEPSuccessful = TurnOffEP -SiteVDirLocation $SiteVDirLocation - - if ($null -ne $IpRangesForFiltering) { - try { - $baseError = "Installation of IP and Domain filtering Module failed." - $InstallResult = Install-WindowsFeature Web1-IP-Security -ErrorAction Stop -WhatIf:$WhatIf - if (-not $InstallResult.Success) { - throw $baseError - } - } catch { - throw "$baseError Inner exception: $_" + try { + $baseError = "Installation of IP and Domain filtering Module failed." + $InstallResult = Install-WindowsFeature Web1-IP-Security -ErrorAction Stop -WhatIf:$WhatIf + if (-not $InstallResult.Success) { + throw $baseError } + } catch { + throw "$baseError Inner exception: $_" + } - $results.IsWindowsFeatureInstalled = $true + $results.IsWindowsFeatureInstalled = $true - $localIPs = Get-LocalIpAddresses - $results.IsGetLocalIPSuccessful = $true + $localIPs = Get-LocalIpAddresses + $results.IsGetLocalIPSuccessful = $true - foreach ($localIP in $localIPs) { - if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP })) { - $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } - } + foreach ($localIP in $localIPs) { + if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP })) { + $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } } - - $results.LocalIPs = $localIPs - CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results } + + $results.LocalIPs = $localIPs + CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results } catch { $results.ErrorContext = $_ } @@ -224,21 +203,8 @@ function Invoke-ConfigureMitigation { Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs - #Write-Host ("Setting Extended protection flag to None on Server {0}" -f $Server) - if ($resultsInvoke.IsTurnOffEPSuccessful) { - Write-Verbose ("Successfully turned Off Extended Protection on server {0}" -f $Server) - } else { - Write-Host ("Script failed to Turn Off Extended protection on server {0} with the Inner Exception:" -f $Server) -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext - $FailedServersEP += $Server - $FailedServersFilter += $Server - continue - } - - if ($null -eq $ipRangeAllowListRules) { - continue - } - + Write-Host ("Adding IP Restriction rules on Server {0}") -f $Server + Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) if ($resultsInvoke.IsWindowsFeatureInstalled) { Write-Verbose ("Successfully installed windows feature - Web-IP-Security on server {0}" -f $Server) } else { @@ -295,7 +261,7 @@ function Invoke-ConfigureMitigation { continue } - Write-Host ("Enabled ip filtering rules on server {0}" -f $Server) + Write-Host ("Enabled ip filtering rules on server {0}" -f $Server) } } end { if ($FailedServersEP.Length -gt 0) { diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index 48ee6a55b0..e4f182f886 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -30,18 +30,8 @@ function Get-IPRangeAllowListFromFile { } if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { - $SubnetStrings = New-Object 'System.Collections.Generic.List[string]' - Write-Warning "The provided file is empty." - $params = @{ - Message = "Display Warning about using an empty ip file for ip filtering" - Target = "The file provided to create the ip filtering allow list is empty." + - " Using this will block all external inbound connections." + - "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc. Do you want to proceed?" - Operation = "Enabling IP Filtering Mitigation" - } - - Show-Disclaimer @params - $ipRangesString = "{}" + Write-Host "The file provided is empty. Please provide a valid file." -ForegroundColor Red + return } else { $ipRangesString = [string]::Join(", ", $SubnetStrings) } @@ -53,9 +43,6 @@ function Get-IPRangeAllowListFromFile { try { foreach ($SubnetString in $SubnetStrings) { $SubnetString = $SubnetString.Trim() - if ([string]::IsNullOrEmpty($SubnetString)) { - continue - } $IpAddressString = $SubnetString.Split("/")[0] $SubnetMaskString = $SubnetString.Split("/")[1] diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 57db91b8b8..ee4aa28d7a 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -54,8 +54,11 @@ param( })] [string]$OutputFilePath = [System.IO.Path]::Combine((Get-Location).Path, "IPList.txt"), - [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to apply IP filters.")] - [Parameter (Mandatory = $false, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to validate IP filters.")] + [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to apply IP filters.")] + [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this parameter will allow you to specify a txt file with IP range that will be used to validate IP filters.")] + [ValidateScript({ + (Test-Path -Path $_) + })] [string]$IPRange, [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir on which you want to configure mitigation.")] @@ -116,6 +119,13 @@ begin { "MAPI-nspiBackend" ="Exchange Back End/MAPI/nspi" } + if ($RestrictType -ne "EWSBackend") { + # Currently this code path won't hit but it is just to indicate that in future if we want to add + # additional restict type for some other Vdirs we will have to make changes to Get-ExtendedProtectionConfiguration + Write-Host "Invalid RestrictType" + return + } + $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` -AppendDateTimeToFileName $false ` -ErrorAction SilentlyContinue @@ -145,6 +155,7 @@ begin { if (($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")) { if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { + $ConfigureEPSelected = $true $ConfigureMitigationSelected = $true $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] @@ -157,25 +168,20 @@ begin { $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } - if ($PSBoundParameters.ContainsKey("IPRange")) { - $MitigationTypeSelected = 'EPOffAndIPMitigation' - # Get list of IPs in object form from the file specified - $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRange - if ($ipResults.IsError) { - return - } - - $ipRangeAllowListRules = $ipResults.ipRangeAllowListRules - } else { - $MitigationTypeSelected = 'OnlyEPOffMitigation' + # Get list of IPs in object form from the file specified + $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRange + if ($ipResults.IsError) { + return } + + $ipRangeAllowListRules = $ipResults.ipRangeAllowListRules } if ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) { $ConfigureEPSelected = $true } - if ($InternalOption -eq "SkipEWS") { + if ($InternalOption -eq "SkipEWS" -or $PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { Write-Verbose "SkipEWS option enabled." $Script:SkipEWS = $true } else { @@ -251,12 +257,8 @@ begin { } if ($ValidateMitigationSelected) { - if ($MitigationTypeSelected -eq 'EPOffAndIPMitigation') { - # Validate mitigation - Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir - } else { - Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -Site $Site -VDir $VDir - } + # Validate mitigation + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } if ($ShowExtendedProtection) { @@ -297,11 +299,7 @@ begin { return } - if ($ConfigureEPSelected -or $ConfigureMitigationSelected -or $ValidateMitigation) { - if ($ConfigureMitigationSelected) { - Write-Host "IP Restrictions will only be applied on the servers which pass the Extended Protection Prerequisite Check" - } - + if ($ConfigureEPSelected) { $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS if ($null -ne $prerequisitesCheck) { @@ -481,26 +479,22 @@ begin { exit } - if ($ConfigureEPSelected) { - # Configure Extended Protection based on given parameters - # Prior to executing, add back any unsupported versions back into the list - # for onlineSupportedServers, because the are online and we want to revert them. - $unsupportedAndConfiguredServers | ForEach-Object { $onlineSupportedServers.Add($_) } - $extendedProtectionConfigurations = ($onlineSupportedServers | - Where-Object { $_.ComputerName -in $serverNames }).ExtendedProtectionConfiguration + # Configure Extended Protection based on given parameters + # Prior to executing, add back any unsupported versions back into the list + # for onlineSupportedServers, because the are online and we want to revert them. + $unsupportedAndConfiguredServers | ForEach-Object { $onlineSupportedServers.Add($_) } + $extendedProtectionConfigurations = ($onlineSupportedServers | + Where-Object { $_.ComputerName -in $serverNames }).ExtendedProtectionConfiguration - if ($null -ne $extendedProtectionConfigurations) { - Invoke-ConfigureExtendedProtection -ExtendedProtectionConfigurations $extendedProtectionConfigurations - } else { - Write-Host "No servers are online or no Exchange Servers Support Extended Protection." - } - } elseif ($ConfigureMitigationSelected) { - if ($MitigationTypeSelected -eq 'EPOffAndIPMitigation') { - # Apply rules - Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir - } else { - Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $null -Site $Site -VDir $VDir - } + if ($null -ne $extendedProtectionConfigurations) { + Invoke-ConfigureExtendedProtection -ExtendedProtectionConfigurations $extendedProtectionConfigurations + } else { + Write-Host "No servers are online or no Exchange Servers Support Extended Protection." + } + + if ($ConfigureMitigationSelected) { + # Apply rules + Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir } } elseif ($RollbackSelected) { Write-Host "Prerequisite check will be skipped due to Rollback" From f23f1842c13d8c17e82e5eb0aa9d6c78187c577e Mon Sep 17 00:00:00 2001 From: David Paulson Date: Thu, 11 Aug 2022 15:49:41 -0500 Subject: [PATCH 23/65] Add easy override type option to Extended Protection Configuration --- .../Get-ExtendedProtectionConfiguration.ps1 | 50 +++++++++++++++++++ ...t-ExtendedProtectionPrerequisitesCheck.ps1 | 14 ++++-- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 index 6e9e801ac3..d53cfde6f0 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 @@ -10,21 +10,60 @@ function Get-ExtendedProtectionConfiguration { param( [Parameter(Mandatory = $true)] [string]$ComputerName, + [Parameter(Mandatory = $false)] [System.Xml.XmlNode]$ApplicationHostConfig, + [Parameter(Mandatory = $false)] [System.Version]$ExSetupVersion, + [Parameter(Mandatory = $false)] [bool]$IsMailboxServer = $true, + [Parameter(Mandatory = $false)] [bool]$IsClientAccessServer = $true, + [Parameter(Mandatory = $false)] [bool]$ExcludeEWS = $false, + + [Parameter(Mandatory = $false)] + [ValidateSet("EWSBackend")] + [string[]]$MitigationAppliedType, + [Parameter(Mandatory = $false)] [scriptblock]$CatchActionFunction ) begin { + + # TODO: Move this so it isn't duplicated + # matching restrictions + $restrictionToSite = @{ + "APIFrontend" = "Default Web Site/API" + "AutodiscoverFrontend" = "Default Web Site/Autodiscover" + "ECPFrontend" = "Default Web Site/ECP" + "EWSFrontend" = "Default Web Site/EWS" + "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" + "OABFrontend" = "Default Web Site/OAB" + "PowershellFrontend" = "Default Web Site/Powershell" + "OWAFrontend" = "Default Web Site/OWA" + "RPCFrontend" = "Default Web Site/RPC" + "MAPIFrontend" = "Default Web Site/MAPI" + "APIBackend" = "Exchange Back End/API" + "AutodiscoverBackend" = "Exchange Back End/Autodiscover" + "ECPBackend" = "Exchange Back End/ECP" + "EWSBackend" = "Exchange Back End/EWS" + "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" + "OABBackend" = "Exchange Back End/OAB" + "PowershellBackend" = "Exchange Back End/Powershell" + "OWABackend" = "Exchange Back End/OWA" + "RPCBackend" = "Exchange Back End/RPC" + "PushNotificationsBackend" = "Exchange Back End/PushNotifications" + "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" + "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" + "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" + } + function NewVirtualDirMatchingEntry { param( [Parameter(Mandatory = $true)] @@ -56,6 +95,17 @@ function Get-ExtendedProtectionConfiguration { # Set EWS Vdir to None for known issues if ($ExcludeEWS -and $virtualDirectory -eq "EWS") { $ExtendedProtection[$i] = "None" } + if ($null -ne $MitigationAppliedType -and + $MitigationAppliedType.Count -gt 0) { + foreach ($key in $MitigationAppliedType) { + if ($restrictionToSite[$key] -eq "$($WebSite[$i])/$virtualDirectory") { + Write-Verbose "Set Extended Protection to None because of restriction override '$($WebSite[$i])\$virtualDirectory'" + $ExtendedProtection[$i] = "None" + break; + } + } + } + [PSCustomObject]@{ VirtualDirectory = $virtualDirectory WebSite = $WebSite[$i] diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 index 8b30cee556..2428d9641d 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 @@ -11,6 +11,11 @@ function Get-ExtendedProtectionPrerequisitesCheck { param( [Parameter(Mandatory = $true)] [object[]]$ExchangeServers, + + [Parameter(Mandatory = $false)] + [ValidateSet("EWSBackend")] + [string[]]$MitigationAppliedType, + [Parameter(Mandatory = $false)] [bool]$SkipEWS ) @@ -36,10 +41,11 @@ function Get-ExtendedProtectionPrerequisitesCheck { Write-Verbose "$($progressParams.Status)" $params = @{ - ComputerName = $server.ToString() - IsClientAccessServer = $server.IsClientAccessServer - IsMailboxServer = $server.IsMailboxServer - ExcludeEWS = $SkipEWS + ComputerName = $server.ToString() + IsClientAccessServer = $server.IsClientAccessServer + IsMailboxServer = $server.IsMailboxServer + ExcludeEWS = $SkipEWS + MitigationAppliedType = $MitigationAppliedType } $extendedProtectionConfiguration = Get-ExtendedProtectionConfiguration @params From a89dc75fc1372db09af89da9f0f9014a95577c27 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Fri, 12 Aug 2022 11:37:50 +0530 Subject: [PATCH 24/65] incorporated Mitigationappliedtype parameter --- .../ExchangeExtendedProtectionManagement.ps1 | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index ee4aa28d7a..a85b8611dc 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -119,13 +119,6 @@ begin { "MAPI-nspiBackend" ="Exchange Back End/MAPI/nspi" } - if ($RestrictType -ne "EWSBackend") { - # Currently this code path won't hit but it is just to indicate that in future if we want to add - # additional restict type for some other Vdirs we will have to make changes to Get-ExtendedProtectionConfiguration - Write-Host "Invalid RestrictType" - return - } - $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` -AppendDateTimeToFileName $false ` -ErrorAction SilentlyContinue @@ -138,6 +131,7 @@ begin { $ConfigureMitigationSelected = $false $ValidateMitigationSelected = $false $Script:SkipEWS = $false + $MitigationAppliedType = New-Object 'System.Collections.Generic.List[string]' $includeExchangeServerNames = New-Object 'System.Collections.Generic.List[string]' if ($PsCmdlet.ParameterSetName -eq "Rollback") { @@ -159,6 +153,7 @@ begin { $ConfigureMitigationSelected = $true $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] + $MitigationAppliedType += $RestrictType } if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { @@ -181,7 +176,7 @@ begin { $ConfigureEPSelected = $true } - if ($InternalOption -eq "SkipEWS" -or $PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { + if ($InternalOption -eq "SkipEWS") { Write-Verbose "SkipEWS option enabled." $Script:SkipEWS = $true } else { @@ -232,8 +227,6 @@ begin { Show-Disclaimer @params } - # TODO : Show some disclaimer for mitigation script - Write-Verbose ("Running Get-ExchangeServer to get list of all exchange servers") Set-ADServerSettings -ViewEntireForest $true $ExchangeServers = Get-ExchangeServer | Where-Object { $_.AdminDisplayVersion -like "Version 15*" -and $_.ServerRole -ne "Edge" } @@ -266,10 +259,11 @@ begin { $extendedProtectionConfigurations = New-Object 'System.Collections.Generic.List[object]' foreach ($server in $ExchangeServers) { $params = @{ - ComputerName = $server.ToString() - IsClientAccessServer = $server.IsClientAccessServer - IsMailboxServer = $server.IsMailboxServer - ExcludeEWS = $SkipEWS + ComputerName = $server.ToString() + IsClientAccessServer = $server.IsClientAccessServer + IsMailboxServer = $server.IsMailboxServer + ExcludeEWS = $SkipEWS + MitigationAppliedType = $MitigationAppliedType } $extendedProtectionConfigurations.Add((Get-ExtendedProtectionConfiguration @params)) } @@ -300,7 +294,7 @@ begin { } if ($ConfigureEPSelected) { - $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS + $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS -MitigationAppliedType $MitigationAppliedType if ($null -ne $prerequisitesCheck) { Write-Host "" From 2fa703410d37a85a392188f83c4cce4bd4e3731c Mon Sep 17 00:00:00 2001 From: akshar Date: Sat, 13 Aug 2022 12:18:19 +0530 Subject: [PATCH 25/65] Rollback whatif, Rollback logging change, Configure ip rules limit check --- .../Invoke-ConfigureMitigation.ps1 | 5 +++++ .../Invoke-RollbackIPFiltering.ps1 | 19 ++++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 2b424207d6..93ab10c68f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -120,6 +120,11 @@ function Invoke-ConfigureMitigation { } } + if ($RulesToBeAdded.Count + $ExistingRules.Count -gt 500) { + $results.IPsNotAdded += $RulesToBeAdded + throw 'Too many IP filtering rules (Existing rules [$($ExistingRules.Count)] + New rules [$($RulesToBeAdded.Count)] > 500). Please reduce the specified entries by providing appropriate subnets.' + } + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf $results.IsCreateIPRulesSuccessful = $true diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 736e05e3d0..3a32e1b5e5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -32,6 +32,7 @@ function Invoke-RollbackIPFiltering { $Site = $Arguments.Site $VDir = $Arguments.VDir + $WhatIf = $Arguments.PassedWhatIf $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' @@ -61,7 +62,10 @@ function Invoke-RollbackIPFiltering { } $BackupFilteringConfiguration = @{Rules=$ExistingRules; DefaultForUnspecifiedIPs=$DefaultForUnspecifiedIPs } - $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + if (-not $WhatIf) { + $BackupFilteringConfiguration | ConvertTo-Json -Depth 2 | Out-File $BackupPath + } + return $true } @@ -71,13 +75,13 @@ function Invoke-RollbackIPFiltering { $DefaultForUnspecifiedIPs ) - Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop + Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop -WhatIf:$WhatIf $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' foreach ($IpFilteringRule in $OriginalIpFilteringRules) { $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } } - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value -WhatIf:$WhatIf + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf return $true } @@ -104,8 +108,9 @@ function Invoke-RollbackIPFiltering { Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { $scriptblockArgs = [PSCustomObject]@{ - Site = $Site - VDir = $VDir + Site = $Site + VDir = $VDir + PassedWhatIf = $WhatIfPreference } $exchangeServersProcessed = 0 @@ -118,7 +123,7 @@ function Invoke-RollbackIPFiltering { $exchangeServersProcessed++; Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with Arguments Site: {1}, VDir: {2}" -f $Server.Name, $Site, $VDir) - Write-Host ("Restoring previous state for Server {0}" -f $Server.Name) + Write-Verbose ("Restoring previous state for Server {0}" -f $Server.Name) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs $Failed = $false From 0391c52a79cab3fb4d01bb6fa82c9fa78e6efab1 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Sun, 14 Aug 2022 02:27:41 +0530 Subject: [PATCH 26/65] Changed Restrict type to array for invoke configure mitigation and invoke validate mitigation --- .../Invoke-ConfigureMitigation.ps1 | 193 ++++++++---- .../Invoke-ValidateMitigation.ps1 | 285 ++++++++++-------- .../Get-ExtendedProtectionConfiguration.ps1 | 1 - ...t-ExtendedProtectionPrerequisitesCheck.ps1 | 1 - .../ExchangeExtendedProtectionManagement.ps1 | 66 ++-- 5 files changed, 317 insertions(+), 229 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 93ab10c68f..bd8cf5b83e 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -13,13 +13,39 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] - [string]$Site, - [Parameter(Mandatory = $true)] - [string]$VDir + [string[]]$MitigationAppliedType ) begin { - $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' + # TODO: Move this so it isn't duplicated + # matching restrictions + $restrictionToSite = @{ + "APIFrontend" = "Default Web Site/API" + "AutodiscoverFrontend" = "Default Web Site/Autodiscover" + "ECPFrontend" = "Default Web Site/ECP" + "EWSFrontend" = "Default Web Site/EWS" + "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" + "OABFrontend" = "Default Web Site/OAB" + "PowershellFrontend" = "Default Web Site/Powershell" + "OWAFrontend" = "Default Web Site/OWA" + "RPCFrontend" = "Default Web Site/RPC" + "MAPIFrontend" = "Default Web Site/MAPI" + "APIBackend" = "Exchange Back End/API" + "AutodiscoverBackend" = "Exchange Back End/Autodiscover" + "ECPBackend" = "Exchange Back End/ECP" + "EWSBackend" = "Exchange Back End/EWS" + "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" + "OABBackend" = "Exchange Back End/OAB" + "PowershellBackend" = "Exchange Back End/Powershell" + "OWABackend" = "Exchange Back End/OWA" + "RPCBackend" = "Exchange Back End/RPC" + "PushNotificationsBackend" = "Exchange Back End/PushNotifications" + "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" + "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" + "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" + } + + $FailedServersFilter = @{} $progressParams = @{ Activity = "Applying IP filtering Rules" @@ -34,27 +60,29 @@ function Invoke-ConfigureMitigation { [Object]$Arguments ) - $SiteVDirLocation = $Arguments.SiteVDirLocation + $SiteVDirLocations = $Arguments.SiteVDirLocations $IpRangesForFiltering = $Arguments.IpRangesForFiltering $WhatIf = $Arguments.PassedWhatIf - $Filter = 'system.webServer/security/ipSecurity' - $IISPath = 'IIS:\' - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection $results = @{ - IsWindowsFeatureInstalled = $false - IsGetLocalIPSuccessful = $false - IsBackUpSuccessful = $false - IsCreateIPRulesSuccessful = $false - IsSetDefaultRuleSuccessful = $false - ErrorContext = $null - IPsNotAdded = New-Object 'System.Collections.Generic.List[string]' - LocalIPs = New-Object 'System.Collections.Generic.List[string]' + IsWindowsFeatureInstalled = $false + IsGetLocalIPSuccessful = $false + LocalIPs = New-Object 'System.Collections.Generic.List[string]' + ErrorContext = $null } function Backup-currentIpFilteringRules { param( - $BackupPath + [Parameter(Mandatory = $true)] + [string]$BackupPath, + [Parameter(Mandatory = $true)] + [string]$Filter, + [Parameter(Mandatory = $true)] + [string]$IISPath, + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $false)] + [object[]]$ExistingRules ) $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" @@ -90,11 +118,14 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [object[]]$IpFilteringRules, [Parameter(Mandatory = $true)] - [hashtable] $results + [hashtable] $state ) $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" - $results.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath + $Filter = 'system.webServer/security/ipSecurity' + $IISPath = 'IIS:\' + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $state.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' @@ -115,28 +146,31 @@ function Invoke-ConfigureMitigation { $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) } - $results.IPsNotAdded += $IpString + $state.IPsNotAdded += $IpString } } } if ($RulesToBeAdded.Count + $ExistingRules.Count -gt 500) { - $results.IPsNotAdded += $RulesToBeAdded + $state.IPsNotAdded += $RulesToBeAdded throw 'Too many IP filtering rules (Existing rules [$($ExistingRules.Count)] + New rules [$($RulesToBeAdded.Count)] > 500). Please reduce the specified entries by providing appropriate subnets.' } - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf - $results.IsCreateIPRulesSuccessful = $true + if ($RulesToBeAdded.Count -gt 0) { + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf + } + + $state.IsCreateIPRulesSuccessful = $true # Setting default to deny Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $false -WhatIf:$WhatIf - $results.IsSetDefaultRuleSuccessful = $true + $state.IsSetDefaultRuleSuccessful = $true } try { try { $baseError = "Installation of IP and Domain filtering Module failed." - $InstallResult = Install-WindowsFeature Web1-IP-Security -ErrorAction Stop -WhatIf:$WhatIf + $InstallResult = Install-WindowsFeature Web-IP-Security -ErrorAction Stop -WhatIf:$WhatIf if (-not $InstallResult.Success) { throw $baseError } @@ -156,7 +190,23 @@ function Invoke-ConfigureMitigation { } $results.LocalIPs = $localIPs - CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results + foreach ($SiteVDirLocation in $SiteVDirLocations) { + $state = @{ + IsBackUpSuccessful = $false + IsCreateIPRulesSuccessful = $false + IsSetDefaultRuleSuccessful = $false + ErrorContext = $null + IPsNotAdded = New-Object 'System.Collections.Generic.List[string]' + } + + try { + CreateIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -state $state + } catch { + $state.ErrorContext = $_ + } + + $results[$SiteVDirLocation] = $state + } } catch { $results.ErrorContext = $_ } @@ -178,13 +228,20 @@ function Invoke-ConfigureMitigation { return $string.Trim(", ") } } process { - $SiteVDirLocation = $Site - if ($VDir -ne '') { - $SiteVDirLocation += '/' + $VDir + $SiteVDirLocations = New-Object 'System.Collections.Generic.List[string]' + foreach ($key in $MitigationAppliedType) { + $Site = $restrictionToSite[$key].Split("/")[0] + $VDir = $restrictionToSite[$key].Split("/")[1] + $SiteVDirLocation = $Site + if ($VDir -ne '') { + $SiteVDirLocation += '/' + $VDir + } + + $SiteVDirLocations += $SiteVDirLocation } $scriptblockArgs = [PSCustomObject]@{ - SiteVDirLocation = $SiteVDirLocation + SiteVDirLocations = $SiteVDirLocations IpRangesForFiltering = $ipRangeAllowListRules PassedWhatIf = $WhatIfPreference } @@ -208,7 +265,6 @@ function Invoke-ConfigureMitigation { Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs - Write-Host ("Adding IP Restriction rules on Server {0}") -f $Server Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) if ($resultsInvoke.IsWindowsFeatureInstalled) { Write-Verbose ("Successfully installed windows feature - Web-IP-Security on server {0}" -f $Server) @@ -233,48 +289,51 @@ function Invoke-ConfigureMitigation { continue } - if ($resultsInvoke.IsBackUpSuccessful) { - Write-Verbose ("Successfully backed up IP filtering allow list") - } else { - Write-Host ("Script failed to backup IP filtering allow list with the Inner Exception:") -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext - $FailedServersFilter += $Server - continue - } + foreach ($SiteVDirLocation in $SiteVDirLocations) { + $state = $resultsInvoke[$SiteVDirLocation] + $FailedServersFilter[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' - if ($resultsInvoke.IsCreateIPRulesSuccessful) { - Write-Verbose ("Successfully updated IP filtering allow list") - if ($resultsInvoke.IPsNotAdded.Length -gt 0) { - $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") - Write-Warning ($line + "Check logs for further details.") - Write-Verbose $line - Write-Verbose (GetCommaSaperatedString -list $resultsInvoke.IPsNotAdded) + if ($state.IsBackUpSuccessful) { + Write-Verbose ("Successfully backed up IP filtering allow list for VDir $SiteVDirLocation") + } else { + Write-Host ("Script failed to backup IP filtering allow list for VDir $SiteVDirLocation with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $state.ErrorContext + $FailedServersFilter[$SiteVDirLocation] += $Server + continue } - } else { - Write-Host ("Script failed to update IP filtering allow list with the Inner Exception:") -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext - $FailedServersFilter += $Server - continue - } - if ($resultsInvoke.IsSetDefaultRuleSuccessful) { - Write-Verbose ("Successfully set the default IP filtering rule to deny") - } else { - Write-Host ("Script failed to set the default IP filtering rule to deny with the Inner Exception:") -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext - $FailedServersFilter += $Server - continue - } + if ($state.IsCreateIPRulesSuccessful) { + Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation") + if ($state.IPsNotAdded.Length -gt 0) { + $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") + Write-Warning ($line + "Check logs for further details.") + Write-Verbose $line + Write-Verbose (GetCommaSaperatedString -list $state.IPsNotAdded) + } + } else { + Write-Host ("Script failed to update IP filtering allow list for VDir $SiteVDirLocation. with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $state.ErrorContext + $FailedServersFilter[$SiteVDirLocation] += $Server + continue + } + + if ($state.IsSetDefaultRuleSuccessful) { + Write-Verbose ("Successfully set the default IP filtering rule to deny for VDir $SiteVDirLocation") + } else { + Write-Host ("Script failed to set the default IP filtering rule to deny for VDir $SiteVDirLocation with the Inner Exception:") -ForegroundColor Red + Write-HostErrorInformation $state.ErrorContext + $FailedServersFilter[$SiteVDirLocation] += $Server + continue + } - Write-Host ("Enabled ip filtering rules on server {0}" -f $Server) + Write-Host ("Enabled ip filtering rules on server {0} for VDir $SiteVDirLocation" -f $Server) + } } } end { - if ($FailedServersEP.Length -gt 0) { - Write-Host ("Unable to Turn Off Extended Protection on the following servers: {0}" -f [string]::Join(", ", $FailedServersEP)) -ForegroundColor Red - } - - if ($FailedServersFilter.Length -gt 0) { - Write-Host ("Unable to create IP Filtering Rules on the following servers: {0}" -f [string]::Join(", ", $FailedServersFilter)) -ForegroundColor Red + foreach ($SiteVDirLocation in $SiteVDirLocations) { + if ($FailedServersFilter[$SiteVDirLocation].Length -gt 0) { + Write-Host ("Unable to create IP Filtering Rules for VDir $SiteVDirLocation on the following servers: {0}" -f [string]::Join(", ", $FailedServersFilter[$SiteVDirLocation])) -ForegroundColor Red + } } } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 7d6741b3d5..3b3514e617 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -11,19 +11,45 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, [Parameter(Mandatory = $false)] - $ipRangeAllowListRules, + [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] - [string]$Site, - [Parameter(Mandatory = $true)] - [string]$VDir + [string[]]$MitigationAppliedType ) begin { - $FailedServersEP = New-Object 'System.Collections.Generic.List[string]' - $FailedServersFilter = New-Object 'System.Collections.Generic.List[string]' + # TODO: Move this so it isn't duplicated + # matching restrictions + $restrictionToSite = @{ + "APIFrontend" = "Default Web Site/API" + "AutodiscoverFrontend" = "Default Web Site/Autodiscover" + "ECPFrontend" = "Default Web Site/ECP" + "EWSFrontend" = "Default Web Site/EWS" + "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" + "OABFrontend" = "Default Web Site/OAB" + "PowershellFrontend" = "Default Web Site/Powershell" + "OWAFrontend" = "Default Web Site/OWA" + "RPCFrontend" = "Default Web Site/RPC" + "MAPIFrontend" = "Default Web Site/MAPI" + "APIBackend" = "Exchange Back End/API" + "AutodiscoverBackend" = "Exchange Back End/Autodiscover" + "ECPBackend" = "Exchange Back End/ECP" + "EWSBackend" = "Exchange Back End/EWS" + "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" + "OABBackend" = "Exchange Back End/OAB" + "PowershellBackend" = "Exchange Back End/Powershell" + "OWABackend" = "Exchange Back End/OWA" + "RPCBackend" = "Exchange Back End/RPC" + "PushNotificationsBackend" = "Exchange Back End/PushNotifications" + "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" + "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" + "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" + } + + $FailedServersEP = @{} + $FailedServersFilter = @{} - $UnMitigatedServersEP = New-Object 'System.Collections.Generic.List[string]' - $UnMitigatedServersFilter = New-Object 'System.Collections.Generic.List[string]' + $UnMitigatedServersEP = @{} + $UnMitigatedServersFilter = @{} $progressParams = @{ Activity = "Verifying Mitigations" @@ -31,8 +57,6 @@ function Invoke-ValidateMitigation { PercentComplete = 0 } - $ShouldVerifyFilter = ($null -ne $ipRangeAllowListRules) - Write-Verbose "Calling: $($MyInvocation.MyCommand)" function GetCommaSaperatedString { @@ -53,20 +77,10 @@ function Invoke-ValidateMitigation { [Object]$Arguments ) - $SiteVDirLocation = $Arguments.SiteVDirLocation + $SiteVDirLocations = $Arguments.SiteVDirLocations $IpRangesForFiltering = $Arguments.IpRangesForFiltering - $results = @{ - IsEPVerified = $false - IsEPOff = $false - IsWindowsFeatureInstalled = $false - IsWindowsFeatureVerified = $false - AreIPRulesVerified = $false - IsDefaultFilterVerified = $false - IsDefaultFilterDeny = $false - RulesNotFound = New-Object 'System.Collections.Generic.List[string]' - ErrorContext = $null - } + $results = @{} function Get-LocalIpAddresses { $ips = New-Object 'System.Collections.Generic.List[string]' @@ -101,13 +115,13 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $true)] [object[]]$IpFilteringRules, [Parameter(Mandatory = $true)] - [hashtable]$results + [hashtable]$state ) - $results.IsWindowsFeatureInstalled = (Get-WindowsFeature -Name "Web-IP-Security").InstallState -eq "Installed" - $results.IsWindowsFeatureVerified = $true + $state.IsWindowsFeatureInstalled = (Get-WindowsFeature -Name "Web-IP-Security").InstallState -eq "Installed" + $state.IsWindowsFeatureVerified = $true - if (-not $results.IsWindowsFeatureInstalled) { + if (-not $state.IsWindowsFeatureInstalled) { return } @@ -124,55 +138,83 @@ function Invoke-ValidateMitigation { } else { $IpString = ("{0}/{1}" -f $IpFilteringRule.IP, $IpFilteringRule.SubnetMask) } - $results.RulesNotFound += $IpString + $state.RulesNotFound += $IpString } } - $results.AreIPRulesVerified = $true + $state.AreIPRulesVerified = $true - $results.IsDefaultFilterDeny = -not ((Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted").Value) - $results.IsDefaultFilterVerified = $true + $state.IsDefaultFilterDeny = -not ((Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted").Value) + $state.IsDefaultFilterVerified = $true } - try { - $EPState = GetEPState -SiteVDirLocation $SiteVDirLocation + foreach ($SiteVDirLocation in $SiteVDirLocations) { + try { + $state = @{ + IsEPVerified = $false + IsEPOff = $false + IsWindowsFeatureInstalled = $false + IsWindowsFeatureVerified = $false + AreIPRulesVerified = $false + IsDefaultFilterVerified = $false + IsDefaultFilterDeny = $false + RulesNotFound = New-Object 'System.Collections.Generic.List[string]' + ErrorContext = $null + } - if ($EPState -eq "None") { - $results.IsEPOff = $true - } else { - $results.IsEPOff = $false - } + $EPState = GetEPState -SiteVDirLocation $SiteVDirLocation + if ($EPState -eq "None") { + $state.IsEPOff = $true + } else { + $state.IsEPOff = $false + } - $results.IsEPVerified = $true + $state.IsEPVerified = $true - if ($null -ne $IpRangesForFiltering) { - $localIPs = Get-LocalIpAddresses + if ($null -ne $IpRangesForFiltering) { + $localIPs = Get-LocalIpAddresses - $localIPs | ForEach-Object { - $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } - } + $localIPs | ForEach-Object { + $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } + } - VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -results $results + VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -state $state + } + } catch { + $state.ErrorContext = $_ } - } catch { - $results.ErrorContext = $_ + + $results[$SiteVDirLocation] = $state } return $results } } process { - $SiteVDirLocation = $Site - if ($VDir -ne '') { - $SiteVDirLocation += '/' + $VDir + $SiteVDirLocations = New-Object 'System.Collections.Generic.List[string]' + foreach ($key in $MitigationAppliedType) { + $Site = $restrictionToSite[$key].Split("/")[0] + $VDir = $restrictionToSite[$key].Split("/")[1] + $SiteVDirLocation = $Site + if ($VDir -ne '') { + $SiteVDirLocation += '/' + $VDir + } + + $SiteVDirLocations += $SiteVDirLocation } $scriptblockArgs = [PSCustomObject]@{ - SiteVDirLocation = $SiteVDirLocation + SiteVDirLocations = $SiteVDirLocations IpRangesForFiltering = $ipRangeAllowListRules } $counter = 0 $totalCount = $ExchangeServers.Count + if ($null -eq $ipRangeAllowListRules) { + $ipRangeAllowListString = "null" + } else { + $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) + } + foreach ($Server in $ExchangeServers) { $baseStatus = "Processing: $Server -" $progressParams.PercentComplete = ($counter / $totalCount * 100) @@ -180,91 +222,98 @@ function Invoke-ValidateMitigation { Write-Progress @progressParams $counter ++; - if ($null -eq $ipRangeAllowListRules) { - $ipRangeAllowListString = "null" - } else { - $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) - } - - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocations: {1}, ipRangeAllowListRules: {2}" -f $Server, [string]::Join(", ", $SiteVDirLocations), $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs + foreach ($SiteVDirLocation in $SiteVDirLocations) { + $state = $resultsInvoke[$SiteVDirLocation] + $FailedServersEP[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' + $FailedServersFilter[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' + + $UnMitigatedServersEP[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' + $UnMitigatedServersFilter[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' + + if ($state.IsEPOff) { + Write-Verbose ("Expected: The state of Extended protection flag is None for Vdir: $($SiteVDirLocation)") + } elseif ($state.IsEPVerified) { + Write-Host ("Unexpected: The state of Extended protection flag is not set to None for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + $UnMitigatedServersEP[$SiteVDirLocation] += $Server + } else { + Write-Host ("Unknown: Script failed to get state of Extended protection flag for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersEP[$SiteVDirLocation] += $Server + $FailedServersFilter[$SiteVDirLocation] += $Server + continue + } - Write-Host ("Validating state of Extended protection flag on Server {0}" -f $Server) - if ($resultsInvoke.IsEPOff) { - Write-Host ("Expected: The state of Extended protection flag is None") - } elseif ($resultsInvoke.IsEPVerified) { - Write-Host ("Unexpected: The state of Extended protection flag is not set to None") -ForegroundColor Red - $UnMitigatedServersEP += $Server - } else { - Write-Host ("Unknown: Script failed to get state of Extended protection flag with Inner Exception") -ForegroundColor Red - Write-HostErrorInformation $results.ErrorContext - $FailedServersEP += $Server - $FailedServersFilter += $Server - continue - } - - if (-not $ShouldVerifyFilter) { - continue - } + $IsFilterUnMitigated = $false - Write-Host ("Validating IP restrictions on Server {0}" -f $Server) - $IsFilterUnMitigated = $false - - if (-not $resultsInvoke.IsWindowsFeatureVerified) { - Write-Host ("Unknown: Script failed to verify if the Windows feature Web-IP-Security is present with Inner Exception") -ForegroundColor Red - Write-HostErrorInformation $results.ErrorContext - $FailedServersFilter += $Server - continue - } elseif (-not $resultsInvoke.IsWindowsFeatureInstalled) { - Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server") -ForegroundColor Red - $IsFilterUnMitigated = $true - } else { - Write-Host ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server") - if (-not $resultsInvoke.AreIPRulesVerified) { - Write-Host ("Unknown: Script failed to verify IP Filtering Rules with Inner Exception") -ForegroundColor Red + if (-not $state.IsWindowsFeatureVerified) { + Write-Host ("Unknown: Script failed to verify if the Windows feature Web-IP-Security is present for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext - $FailedServersFilter += $Server + $FailedServersFilter[$SiteVDirLocation] += $Server continue - } elseif ($null -ne $resultsInvoke.RulesNotFound -and $resultsInvoke.RulesNotFound.Length -gt 0) { - Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied") -ForegroundColor Red - Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.RulesNotFound)) + } elseif (-not $state.IsWindowsFeatureInstalled) { + Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir: $($SiteVDirLocation)") -ForegroundColor Red $IsFilterUnMitigated = $true } else { - Write-Host ("Expected: Successfully verified all the IP filtering rules") - } + Write-Verbose ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server for Vdir: $($SiteVDirLocation)") + if (-not $state.AreIPRulesVerified) { + Write-Host ("Unknown: Script failed to verify IP Filtering Rules for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersFilter[$SiteVDirLocation] += $Server + continue + } elseif ($null -ne $state.RulesNotFound -and $state.RulesNotFound.Length -gt 0) { + Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $state.RulesNotFound)) + $IsFilterUnMitigated = $true + } else { + Write-Verbose ("Expected: Successfully verified all the IP filtering rules for Vdir: $($SiteVDirLocation)") + } - if ($resultsInvoke.IsDefaultFilterDeny) { - Write-Host ("Expected: The default IP Filtering rule is set to deny") - } elseif ($resultsInvoke.IsDefaultFilterVerified) { - Write-Host ("Unexpected: The default IP Filtering rule is not set to deny") -ForegroundColor Red - $IsFilterUnMitigated = $true - } else { - Write-Host ("Unknown: Script failed to get the default IP Filtering rule with Inner Exception") -ForegroundColor Red - Write-HostErrorInformation $results.ErrorContext - $FailedServersFilter += $Server - continue + if ($state.IsDefaultFilterDeny) { + Write-Verbose ("Expected: The default IP Filtering rule is set to deny for Vdir: $($SiteVDirLocation)") + } elseif ($state.IsDefaultFilterVerified) { + Write-Host ("Unexpected: The default IP Filtering rule is not set to deny for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + $IsFilterUnMitigated = $true + } else { + Write-Host ("Unknown: Script failed to get the default IP Filtering rule for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-HostErrorInformation $results.ErrorContext + $FailedServersFilter[$SiteVDirLocation] += $Server + continue + } } - } - if ($IsFilterUnMitigated) { - $UnMitigatedServersFilter += $Server + if ($IsFilterUnMitigated) { + $UnMitigatedServersFilter[$SiteVDirLocation] += $Server + } } } } end { - if ($UnMitigatedServersEP.Length -gt 0) { - Write-Host ("Extended Protection on the following servers are not set to expected values: {0}" -f [string]::Join(", ", $UnMitigatedServersEP)) -ForegroundColor Red - } + $FoundFailedOrUnmitigated = $false + foreach ($SiteVDirLocation in $SiteVDirLocations) { + if ($UnMitigatedServersEP[$SiteVDirLocation].Length -gt 0) { + Write-Host ("Extended Protection on the following servers are not set to expected values for VDir {0}: {1}" -f $SiteVDirLocation, [string]::Join(", ", $UnMitigatedServersEP[$SiteVDirLocation])) -ForegroundColor Red + $FoundFailedOrUnmitigated = $true + } - if ($UnMitigatedServersFilter.Length -gt 0) { - Write-Host ("IP Filtering Rules or Default IP rule on the following servers is not as expected: {0}" -f [string]::Join(", ", $UnMitigatedServersFilter)) -ForegroundColor Red - } + if ($UnMitigatedServersFilter[$SiteVDirLocation].Length -gt 0) { + Write-Host ("IP Filtering Rules or Default IP rule on the following servers is not as expected for VDir {0}: {1}" -f $SiteVDirLocation, [string]::Join(", ", $UnMitigatedServersFilter[$SiteVDirLocation])) -ForegroundColor Red + $FoundFailedOrUnmitigated = $true + } - if ($FailedServersEP.Length -gt 0) { - Write-Host ("Unable to verify Extended Protection on the following servers: {0}" -f [string]::Join(", ", $FailedServersEP)) -ForegroundColor Red + if ($FailedServersEP[$SiteVDirLocation].Length -gt 0) { + Write-Host ("Unable to verify Extended Protection on the following servers for VDir {0}: {1}" -f $SiteVDirLocation, [string]::Join(", ", $FailedServersEP[$SiteVDirLocation])) -ForegroundColor Red + $FoundFailedOrUnmitigated = $true + } + + if ($FailedServersFilter[$SiteVDirLocation].Length -gt 0) { + Write-Host ("Unable to verify IP Filtering Rules on the following servers for VDir {0}: {1}" -f $SiteVDirLocation, [string]::Join(", ", $FailedServersFilter[$SiteVDirLocation])) -ForegroundColor Red + $FoundFailedOrUnmitigated = $true + } } - if ($FailedServersFilter.Length -gt 0) { - Write-Host ("Unable to verify IP Filtering Rules on the following servers: {0}" -f [string]::Join(", ", $FailedServersFilter)) -ForegroundColor Red + if (-not $FoundFailedOrUnmitigated) { + Write-Host "All the servers have been validated successfully!" -ForegroundColor Green } } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 index d53cfde6f0..f749c37175 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 @@ -27,7 +27,6 @@ function Get-ExtendedProtectionConfiguration { [bool]$ExcludeEWS = $false, [Parameter(Mandatory = $false)] - [ValidateSet("EWSBackend")] [string[]]$MitigationAppliedType, [Parameter(Mandatory = $false)] diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 index 2428d9641d..9b31d9b2ba 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 @@ -13,7 +13,6 @@ function Get-ExtendedProtectionPrerequisitesCheck { [object[]]$ExchangeServers, [Parameter(Mandatory = $false)] - [ValidateSet("EWSBackend")] [string[]]$MitigationAppliedType, [Parameter(Mandatory = $false)] diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index a85b8611dc..f6c33c5048 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -40,6 +40,7 @@ param( [string[]]$SkipExchangeServerNames = $null, [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] + [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] [switch]$ShowExtendedProtection, [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Used for internal options")] @@ -63,11 +64,17 @@ param( [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir on which you want to configure mitigation.")] [ValidateSet('EWSBackend')] - [string]$RestrictType, + [ValidateScript({ + ($null -ne $_) -and ($_.Length -gt 0) + })] + [string[]]$RestrictType, [Parameter (Mandatory = $true, ParameterSetName = 'ValidateMitigation', HelpMessage = "Using this switch will allow you to validate if the mitigations have been applied correctly.")] [ValidateSet('RestrictTypeEWSBackend')] - [string]$ValidateMitigation, + [ValidateScript({ + ($null -ne $_) -and ($_.Length -gt 0) + })] + [string[]]$ValidateMitigation, [Parameter (Mandatory = $true, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [ValidateSet('RestrictTypeEWSBackend', 'RestoreIISAppConfig')] @@ -93,32 +100,6 @@ begin { . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 - $RestrictTypeToSiteVDirMap = @{ - "APIFrontend" ="Default Web Site/API" - "AutodiscoverFrontend" ="Default Web Site/Autodiscover" - "ECPFrontend" ="Default Web Site/ECP" - "EWSFrontend" ="Default Web Site/EWS" - "Microsoft-Server-ActiveSyncFrontend" ="Default Web Site/Microsoft-Server-ActiveSync" - "OABFrontend" ="Default Web Site/OAB" - "PowershellFrontend" ="Default Web Site/Powershell" - "OWAFrontend" ="Default Web Site/OWA" - "RPCFrontend" ="Default Web Site/RPC" - "MAPIFrontend" ="Default Web Site/MAPI" - "APIBackend" ="Exchange Back End/API" - "AutodiscoverBackend" ="Exchange Back End/Autodiscover" - "ECPBackend" ="Exchange Back End/ECP" - "EWSBackend" ="Exchange Back End/EWS" - "Microsoft-Server-ActiveSyncBackend" ="Exchange Back End/Microsoft-Server-ActiveSync" - "OABBackend" ="Exchange Back End/OAB" - "PowershellBackend" ="Exchange Back End/Powershell" - "OWABackend" ="Exchange Back End/OWA" - "RPCBackend" ="Exchange Back End/RPC" - "PushNotificationsBackend" ="Exchange Back End/PushNotifications" - "RPCWithCertBackend" ="Exchange Back End/RPCWithCert" - "MAPI-emsmdbBackend" ="Exchange Back End/MAPI/emsmdb" - "MAPI-nspiBackend" ="Exchange Back End/MAPI/nspi" - } - $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` -AppendDateTimeToFileName $false ` -ErrorAction SilentlyContinue @@ -131,19 +112,21 @@ begin { $ConfigureMitigationSelected = $false $ValidateMitigationSelected = $false $Script:SkipEWS = $false - $MitigationAppliedType = New-Object 'System.Collections.Generic.List[string]' $includeExchangeServerNames = New-Object 'System.Collections.Generic.List[string]' if ($PsCmdlet.ParameterSetName -eq "Rollback") { $RollbackSelected = $true - if ($RollbackType -eq "RestoreIISAppConfig") { + if ($RollbackType.Contains("RestoreIISAppConfig")) { + if ($RollbackType.Length -gt 1) { + Write-Host "RestoreIISAppConfig Rollback type can only be used individually" + return + } + $RollbackRestoreIISAppConfig = $true } else { $RollbackRestrictType = $true $RestrictType = $RollbackType.Replace("RestrictType", "") - $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] - $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] } } @@ -151,16 +134,13 @@ begin { if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { $ConfigureEPSelected = $true $ConfigureMitigationSelected = $true - $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] - $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] - $MitigationAppliedType += $RestrictType + $RestrictType = $RestrictType | Get-Unique } if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { $ValidateMitigationSelected = $true - $RestrictType = $ValidateMitigation.Replace("RestrictType", "") - $Site = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[0] - $VDir = $RestrictTypeToSiteVDirMap[$RestrictType].Split("/", 2)[1] + $RestrictType = New-Object 'System.Collections.Generic.List[string]' + $ValidateMitigation | Get-Unique | ForEach-Object { $RestrictType += $_.Replace("RestrictType", "") } } # Get list of IPs in object form from the file specified @@ -251,7 +231,7 @@ begin { if ($ValidateMitigationSelected) { # Validate mitigation - Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -MitigationAppliedType $RestrictType } if ($ShowExtendedProtection) { @@ -263,7 +243,7 @@ begin { IsClientAccessServer = $server.IsClientAccessServer IsMailboxServer = $server.IsMailboxServer ExcludeEWS = $SkipEWS - MitigationAppliedType = $MitigationAppliedType + MitigationAppliedType = $RestrictType } $extendedProtectionConfigurations.Add((Get-ExtendedProtectionConfiguration @params)) } @@ -294,7 +274,7 @@ begin { } if ($ConfigureEPSelected) { - $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS -MitigationAppliedType $MitigationAppliedType + $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS -MitigationAppliedType $RestrictType if ($null -ne $prerequisitesCheck) { Write-Host "" @@ -488,7 +468,7 @@ begin { if ($ConfigureMitigationSelected) { # Apply rules - Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -Site $Site -VDir $VDir + Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -MitigationAppliedType $RestrictType } } elseif ($RollbackSelected) { Write-Host "Prerequisite check will be skipped due to Rollback" @@ -498,6 +478,8 @@ begin { } if ($RollbackRestrictType) { + $Site = $RestrictType[0].Split("/", 2)[0] + $VDir = $RestrictType[0].Split("/", 2)[1] Invoke-RollbackIPFiltering -ExchangeServers $ExchangeServers -Site $Site -VDir $VDir } return From 037cfe15f13ee1da7778f1ca16e8e7c2490362d2 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Mon, 15 Aug 2022 15:42:52 +0530 Subject: [PATCH 27/65] trun on ep during rollback --- .../Invoke-RollbackIPFiltering.ps1 | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 3a32e1b5e5..1e87742a28 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -17,6 +17,7 @@ function Invoke-RollbackIPFiltering { ) begin { + Write-Verbose "Calling: $($MyInvocation.MyCommand)" $FailedServers = New-Object 'System.Collections.Generic.List[string]' $progressParams = @{ @@ -42,6 +43,7 @@ function Invoke-RollbackIPFiltering { } $results = @{ + TurnOnEPSuccessful = $false RestoreFileExists = $false BackUpPath = $null BackupCurrentSuccessful = $false @@ -86,6 +88,13 @@ function Invoke-RollbackIPFiltering { return $true } + function TurnONEP { + $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking + if ($ExtendedProtection -ne "Require") { + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "Require" + } + } + try { $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/', '-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName if ($null -eq $results.RestorePath) { @@ -93,6 +102,9 @@ function Invoke-RollbackIPFiltering { } $results.RestoreFileExists = $true + TurnONEP + $results.TurnOnEPSuccessful = $true + $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath @@ -104,8 +116,6 @@ function Invoke-RollbackIPFiltering { return $results } - - Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { $scriptblockArgs = [PSCustomObject]@{ Site = $Site @@ -128,17 +138,24 @@ function Invoke-RollbackIPFiltering { $Failed = $false if ($resultsInvoke.RestoreFileExists) { - if ($resultsInvoke.BackupCurrentSuccessful) { - Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" - if ($resultsInvoke.RestoreSuccessful) { - Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" + if($resultsInvoke.TurnOnEPSuccessful) { + Write-Host "Turned on EP on server $($Server.Name)" + if ($resultsInvoke.BackupCurrentSuccessful) { + Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" + if ($resultsInvoke.RestoreSuccessful) { + Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" + } else { + Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $resultsInvoke.ErrorContext + $Failed = $true + } } else { - Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $Failed = $true } } else { - Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red + Write-Host "Failed to turn on EP on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $Failed = $true } From 885f8b150555e479dcfc3f08ee60c81aedd7cb18 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Mon, 15 Aug 2022 17:26:47 +0530 Subject: [PATCH 28/65] String changes --- .../DataCollection/Get-IPRangeAllowListFromFile.ps1 | 9 ++++++--- .../ExchangeExtendedProtectionManagement.ps1 | 8 +++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index e4f182f886..9966f20736 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -30,7 +30,7 @@ function Get-IPRangeAllowListFromFile { } if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { - Write-Host "The file provided is empty. Please provide a valid file." -ForegroundColor Red + Write-Host "The Ip range file provided is empty. Please provide a valid file." -ForegroundColor Red return } else { $ipRangesString = [string]::Join(", ", $SubnetStrings) @@ -61,15 +61,18 @@ function Get-IPRangeAllowListFromFile { if ($SubnetMaskString) { # Check if the subnet value is valid (IPv4 <= 32, IPv6 <= 128 or empty) $SubnetMask = $SubnetMaskString -as [int] + + $InvalidSubnetMaskString = "Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range.Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128. Re-execute the command with proper input file for IPRange parameter." if ($null -eq $SubnetMask) { - Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid Subnet Mask found: Unable to parse Subnet Mask {0}. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red + Write-Host ($InvalidSubnetMaskString -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true return $results } elseif (($SubnetMask -gt 32 -and -not $IsIPv6) -or $SubnetMask -gt 128 -or $SubnetMask -lt 0) { - Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range. Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128." -f $SubnetMaskString) -ForegroundColor Red + Write-Host ($InvalidSubnetMaskString -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true return $results } + if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString })) { $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } } else { diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index f6c33c5048..d61d9b208c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -192,12 +192,18 @@ begin { } if ($ConfigureEPSelected) { + + $ArchivingKnownIssueString = "`r`n - Automated Archiving using Archive policy" + if($ConfigureMitigationSelected){ + $ArchivingKnownIssueString = "" + } + $params = @{ Message = "Display Warning about Extended Protection" Target = "Extended Protection is recommended to be enabled for security reasons. " + "Known Issues: Following scenarios will not work when Extended Protection is enabled." + "`r`n - SSL offloading or SSL termination via Layer 7 load balancing." + - "`r`n - Automated Archiving using Archive policy" + + $ArchivingKnownIssueString + "`r`n - Exchange Hybrid Features if using Modern Hybrid." + "`r`n - Access to Public folders on Exchange 2013 Servers." + "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc. Do you want to proceed?" From f67af7696d2e19e36505b9870c4b766cff9cacbe Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Mon, 15 Aug 2022 20:51:55 +0530 Subject: [PATCH 29/65] Made restrict type as list for backing up IP filtering rules Code cleanup --- .../Invoke-ConfigureMitigation.ps1 | 42 +---- .../Invoke-RollbackIPFiltering.ps1 | 157 ++++++++++-------- .../Invoke-ValidateMitigation.ps1 | 42 +---- .../Get-ExtendedProtectionConfiguration.ps1 | 39 +---- ...t-ExtendedProtectionPrerequisitesCheck.ps1 | 12 +- .../Get-IPRangeAllowListFromFile.ps1 | 2 +- .../ExchangeExtendedProtectionManagement.ps1 | 50 +++++- 7 files changed, 148 insertions(+), 196 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index bd8cf5b83e..cdaa6f5d33 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -13,38 +13,10 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] - [string[]]$MitigationAppliedType + [string[]]$SiteVDirLocations ) begin { - # TODO: Move this so it isn't duplicated - # matching restrictions - $restrictionToSite = @{ - "APIFrontend" = "Default Web Site/API" - "AutodiscoverFrontend" = "Default Web Site/Autodiscover" - "ECPFrontend" = "Default Web Site/ECP" - "EWSFrontend" = "Default Web Site/EWS" - "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" - "OABFrontend" = "Default Web Site/OAB" - "PowershellFrontend" = "Default Web Site/Powershell" - "OWAFrontend" = "Default Web Site/OWA" - "RPCFrontend" = "Default Web Site/RPC" - "MAPIFrontend" = "Default Web Site/MAPI" - "APIBackend" = "Exchange Back End/API" - "AutodiscoverBackend" = "Exchange Back End/Autodiscover" - "ECPBackend" = "Exchange Back End/ECP" - "EWSBackend" = "Exchange Back End/EWS" - "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" - "OABBackend" = "Exchange Back End/OAB" - "PowershellBackend" = "Exchange Back End/Powershell" - "OWABackend" = "Exchange Back End/OWA" - "RPCBackend" = "Exchange Back End/RPC" - "PushNotificationsBackend" = "Exchange Back End/PushNotifications" - "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" - "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" - "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" - } - $FailedServersFilter = @{} $progressParams = @{ @@ -228,18 +200,6 @@ function Invoke-ConfigureMitigation { return $string.Trim(", ") } } process { - $SiteVDirLocations = New-Object 'System.Collections.Generic.List[string]' - foreach ($key in $MitigationAppliedType) { - $Site = $restrictionToSite[$key].Split("/")[0] - $VDir = $restrictionToSite[$key].Split("/")[1] - $SiteVDirLocation = $Site - if ($VDir -ne '') { - $SiteVDirLocation += '/' + $VDir - } - - $SiteVDirLocations += $SiteVDirLocation - } - $scriptblockArgs = [PSCustomObject]@{ SiteVDirLocations = $SiteVDirLocations IpRangesForFiltering = $ipRangeAllowListRules diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 1e87742a28..8f25b99bbc 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -11,14 +11,12 @@ function Invoke-RollbackIPFiltering { [Parameter(Mandatory = $true)] [object[]]$ExchangeServers, [Parameter(Mandatory = $true)] - [string]$Site, - [Parameter(Mandatory = $true)] - [string]$VDir + [string[]]$SiteVDirLocations ) begin { Write-Verbose "Calling: $($MyInvocation.MyCommand)" - $FailedServers = New-Object 'System.Collections.Generic.List[string]' + $FailedServers = @{} $progressParams = @{ Activity = "Rolling back IP filtering Rules" @@ -31,33 +29,27 @@ function Invoke-RollbackIPFiltering { [Object]$Arguments ) - $Site = $Arguments.Site - $VDir = $Arguments.VDir + $SiteVDirLocations = $Arguments.SiteVDirLocations $WhatIf = $Arguments.PassedWhatIf $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' - $SiteVDirLocation = $Site - if ($VDir -ne '') { - $SiteVDirLocation += '/' + $VDir - } - - $results = @{ - TurnOnEPSuccessful = $false - RestoreFileExists = $false - BackUpPath = $null - BackupCurrentSuccessful = $false - RestorePath = $null - RestoreSuccessful = $false - ErrorContext = $null - } + $results = @{} function Backup-currentIpFilteringRules { param( - $BackupPath + [Parameter(Mandatory = $true)] + [string]$BackupPath, + [Parameter(Mandatory = $true)] + [string]$Filter, + [Parameter(Mandatory = $true)] + [string]$IISPath, + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $false)] + [object[]]$ExistingRules ) - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" if ($null -eq $ExistingRules) { $ExistingRules = New-Object 'System.Collections.Generic.List[object]' @@ -73,8 +65,16 @@ function Invoke-RollbackIPFiltering { function Restore-OriginalIpFilteringRules { param( - $OriginalIpFilteringRules, - $DefaultForUnspecifiedIPs + [Parameter(Mandatory = $true)] + [string]$Filter, + [Parameter(Mandatory = $true)] + [string]$IISPath, + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation, + [Parameter(Mandatory = $true)] + [object[]]$OriginalIpFilteringRules, + [Parameter(Mandatory = $true)] + [object]$DefaultForUnspecifiedIPs ) Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop -WhatIf:$WhatIf @@ -89,44 +89,65 @@ function Invoke-RollbackIPFiltering { } function TurnONEP { + param( + [Parameter(Mandatory = $true)] + [string]$Filter, + [Parameter(Mandatory = $true)] + [string]$IISPath, + [Parameter(Mandatory = $true)] + [string]$SiteVDirLocation + ) $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking if ($ExtendedProtection -ne "Require") { - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "Require" + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "Require" } } - try { - $results.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/', '-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName - if ($null -eq $results.RestorePath) { - throw "Invalid operation. No backup file exisits at path $($env:WINDIR)\System32\inetsrv\config\" + foreach ($SiteVDirLocation in $SiteVDirLocations) { + $state = @{ + TurnOnEPSuccessful = $false + RestoreFileExists = $false + BackUpPath = $null + BackupCurrentSuccessful = $false + RestorePath = $null + RestoreSuccessful = $false + ErrorContext = $null } - $results.RestoreFileExists = $true + try { + $state.RestorePath = (Get-ChildItem "$($env:WINDIR)\System32\inetsrv\config\" -Filter ("*IpFilteringRules_"+ $SiteVDirLocation.Replace('/', '-') + "*.bak") | Sort-Object CreationTime | Select-Object -First 1).FullName + if ($null -eq $state.RestorePath) { + throw "Invalid operation. No backup file exisits at path $($env:WINDIR)\System32\inetsrv\config\" + } + $state.RestoreFileExists = $true - TurnONEP - $results.TurnOnEPSuccessful = $true + TurnONEP -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation + $state.TurnOnEPSuccessful = $true - $results.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" - $results.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $results.BackUpPath + $state.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" + $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $state.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $state.BackUpPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules - $originalIpFilteringConfigurations = (Get-Content $results.RestorePath | Out-String | ConvertFrom-Json) - $results.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) - } catch { - $results.ErrorContext = $_ + $originalIpFilteringConfigurations = (Get-Content $state.RestorePath | Out-String | ConvertFrom-Json) + $state.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) + } catch { + $state.ErrorContext = $_ + } + + $results[$SiteVDirLocation] = $state } return $results } } process { $scriptblockArgs = [PSCustomObject]@{ - Site = $Site - VDir = $VDir - PassedWhatIf = $WhatIfPreference + SiteVDirLocations = $SiteVDirLocations + PassedWhatIf = $WhatIfPreference } $exchangeServersProcessed = 0 $totalExchangeServers = $ExchangeServers.Count foreach ($Server in $ExchangeServers) { - $baseStatus = "Processing: $($Server.Name) -" # Should this be at the start as the server is already processed? + $baseStatus = "Processing: $($Server.Name) -" $progressParams.PercentComplete = ($exchangeServersProcessed / $totalExchangeServers * 100) $progressParams.Status = "$baseStatus Rolling back rules" Write-Progress @progressParams @@ -135,42 +156,48 @@ function Invoke-RollbackIPFiltering { Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with Arguments Site: {1}, VDir: {2}" -f $Server.Name, $Site, $VDir) Write-Verbose ("Restoring previous state for Server {0}" -f $Server.Name) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs - $Failed = $false - - if ($resultsInvoke.RestoreFileExists) { - if($resultsInvoke.TurnOnEPSuccessful) { - Write-Host "Turned on EP on server $($Server.Name)" - if ($resultsInvoke.BackupCurrentSuccessful) { - Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($resultsInvoke.BackUpPath)" - if ($resultsInvoke.RestoreSuccessful) { - Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($resultsInvoke.RestorePath)" + + foreach ($SiteVDirLocation in $SiteVDirLocations) { + $Failed = $false + $state = $resultsInvoke[$SiteVDirLocation] + $FailedServers[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' + if ($state.RestoreFileExists) { + if ($state.TurnOnEPSuccessful) { + Write-Host "Turned on EP on server $($Server.Name) for VDir $SiteVDirLocation" + if ($state.BackupCurrentSuccessful) { + Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($state.BackUpPath) for VDir $SiteVDirLocation" + if ($state.RestoreSuccessful) { + Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($state.RestorePath) for VDir $SiteVDirLocation" + } else { + Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $state.ErrorContext + $Failed = $true + } } else { - Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext + Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $state.ErrorContext $Failed = $true } } else { - Write-Host "Failed to backup the current configuration on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext + Write-Host "Failed to turn on EP on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $state.ErrorContext $Failed = $true } } else { - Write-Host "Failed to turn on EP on server $($Server.Name). Aborting rollback on the server $($Server.Name). Inner Exception:" -ForegroundColor Red - Write-HostErrorInformation $resultsInvoke.ErrorContext + Write-Host "No restore file exists on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation." -ForegroundColor Red $Failed = $true } - } else { - Write-Host "No restore file exists on server $($Server.Name). Aborting rollback on the server $($Server.Name)." -ForegroundColor Red - $Failed = $true - } - if ($Failed) { - $FailedServers += $Server.Name + if ($Failed) { + $FailedServers[$SiteVDirLocation] += $Server.Name + } } } } end { - if ($FailedServers.Length -gt 0) { - Write-Host ("Unable to rollback for the following servers: {0}" -f [string]::Join(", ", $FailedServers)) -ForegroundColor Red + foreach ($SiteVDirLocation in $SiteVDirLocations) { + if ($FailedServers[$SiteVDirLocation].Length -gt 0) { + Write-Host ("Unable to rollback for VDir $SiteVDirLocation on the following servers: {0}" -f [string]::Join(", ", $FailedServers)) -ForegroundColor Red + } } } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 3b3514e617..c56aa3ff28 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -13,38 +13,10 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $false)] [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] - [string[]]$MitigationAppliedType + [string[]]$SiteVDirLocations ) begin { - # TODO: Move this so it isn't duplicated - # matching restrictions - $restrictionToSite = @{ - "APIFrontend" = "Default Web Site/API" - "AutodiscoverFrontend" = "Default Web Site/Autodiscover" - "ECPFrontend" = "Default Web Site/ECP" - "EWSFrontend" = "Default Web Site/EWS" - "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" - "OABFrontend" = "Default Web Site/OAB" - "PowershellFrontend" = "Default Web Site/Powershell" - "OWAFrontend" = "Default Web Site/OWA" - "RPCFrontend" = "Default Web Site/RPC" - "MAPIFrontend" = "Default Web Site/MAPI" - "APIBackend" = "Exchange Back End/API" - "AutodiscoverBackend" = "Exchange Back End/Autodiscover" - "ECPBackend" = "Exchange Back End/ECP" - "EWSBackend" = "Exchange Back End/EWS" - "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" - "OABBackend" = "Exchange Back End/OAB" - "PowershellBackend" = "Exchange Back End/Powershell" - "OWABackend" = "Exchange Back End/OWA" - "RPCBackend" = "Exchange Back End/RPC" - "PushNotificationsBackend" = "Exchange Back End/PushNotifications" - "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" - "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" - "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" - } - $FailedServersEP = @{} $FailedServersFilter = @{} @@ -190,18 +162,6 @@ function Invoke-ValidateMitigation { return $results } } process { - $SiteVDirLocations = New-Object 'System.Collections.Generic.List[string]' - foreach ($key in $MitigationAppliedType) { - $Site = $restrictionToSite[$key].Split("/")[0] - $VDir = $restrictionToSite[$key].Split("/")[1] - $SiteVDirLocation = $Site - if ($VDir -ne '') { - $SiteVDirLocation += '/' + $VDir - } - - $SiteVDirLocations += $SiteVDirLocation - } - $scriptblockArgs = [PSCustomObject]@{ SiteVDirLocations = $SiteVDirLocations IpRangesForFiltering = $ipRangeAllowListRules diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 index f749c37175..25e122843d 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionConfiguration.ps1 @@ -27,42 +27,13 @@ function Get-ExtendedProtectionConfiguration { [bool]$ExcludeEWS = $false, [Parameter(Mandatory = $false)] - [string[]]$MitigationAppliedType, + [string[]]$SiteVDirLocations, [Parameter(Mandatory = $false)] [scriptblock]$CatchActionFunction ) begin { - - # TODO: Move this so it isn't duplicated - # matching restrictions - $restrictionToSite = @{ - "APIFrontend" = "Default Web Site/API" - "AutodiscoverFrontend" = "Default Web Site/Autodiscover" - "ECPFrontend" = "Default Web Site/ECP" - "EWSFrontend" = "Default Web Site/EWS" - "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" - "OABFrontend" = "Default Web Site/OAB" - "PowershellFrontend" = "Default Web Site/Powershell" - "OWAFrontend" = "Default Web Site/OWA" - "RPCFrontend" = "Default Web Site/RPC" - "MAPIFrontend" = "Default Web Site/MAPI" - "APIBackend" = "Exchange Back End/API" - "AutodiscoverBackend" = "Exchange Back End/Autodiscover" - "ECPBackend" = "Exchange Back End/ECP" - "EWSBackend" = "Exchange Back End/EWS" - "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" - "OABBackend" = "Exchange Back End/OAB" - "PowershellBackend" = "Exchange Back End/Powershell" - "OWABackend" = "Exchange Back End/OWA" - "RPCBackend" = "Exchange Back End/RPC" - "PushNotificationsBackend" = "Exchange Back End/PushNotifications" - "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" - "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" - "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" - } - function NewVirtualDirMatchingEntry { param( [Parameter(Mandatory = $true)] @@ -94,10 +65,10 @@ function Get-ExtendedProtectionConfiguration { # Set EWS Vdir to None for known issues if ($ExcludeEWS -and $virtualDirectory -eq "EWS") { $ExtendedProtection[$i] = "None" } - if ($null -ne $MitigationAppliedType -and - $MitigationAppliedType.Count -gt 0) { - foreach ($key in $MitigationAppliedType) { - if ($restrictionToSite[$key] -eq "$($WebSite[$i])/$virtualDirectory") { + if ($null -ne $SiteVDirLocations -and + $SiteVDirLocations.Count -gt 0) { + foreach ($SiteVDirLocation in $SiteVDirLocations) { + if ($SiteVDirLocation -eq "$($WebSite[$i])/$virtualDirectory") { Write-Verbose "Set Extended Protection to None because of restriction override '$($WebSite[$i])\$virtualDirectory'" $ExtendedProtection[$i] = "None" break; diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 index 9b31d9b2ba..d707d55da4 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExtendedProtectionPrerequisitesCheck.ps1 @@ -13,7 +13,7 @@ function Get-ExtendedProtectionPrerequisitesCheck { [object[]]$ExchangeServers, [Parameter(Mandatory = $false)] - [string[]]$MitigationAppliedType, + [string[]]$SiteVDirLocations, [Parameter(Mandatory = $false)] [bool]$SkipEWS @@ -40,11 +40,11 @@ function Get-ExtendedProtectionPrerequisitesCheck { Write-Verbose "$($progressParams.Status)" $params = @{ - ComputerName = $server.ToString() - IsClientAccessServer = $server.IsClientAccessServer - IsMailboxServer = $server.IsMailboxServer - ExcludeEWS = $SkipEWS - MitigationAppliedType = $MitigationAppliedType + ComputerName = $server.ToString() + IsClientAccessServer = $server.IsClientAccessServer + IsMailboxServer = $server.IsMailboxServer + ExcludeEWS = $SkipEWS + SiteVDirLocations = $SiteVDirLocations } $extendedProtectionConfiguration = Get-ExtendedProtectionConfiguration @params diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index 9966f20736..eb43055418 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -62,7 +62,7 @@ function Get-IPRangeAllowListFromFile { # Check if the subnet value is valid (IPv4 <= 32, IPv6 <= 128 or empty) $SubnetMask = $SubnetMaskString -as [int] - $InvalidSubnetMaskString = "Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range.Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128. Re-execute the command with proper input file for IPRange parameter." + $InvalidSubnetMaskString = "Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range.Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128. Re-execute the command with proper input file for IPRange parameter." if ($null -eq $SubnetMask) { Write-Host ($InvalidSubnetMaskString -f $SubnetMaskString) -ForegroundColor Red $results.IsError = $true diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index d61d9b208c..1df04ab8f7 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -100,6 +100,34 @@ begin { . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 + # TODO: Move this so it isn't duplicated + # matching restrictions + $restrictionToSite = @{ + "APIFrontend" = "Default Web Site/API" + "AutodiscoverFrontend" = "Default Web Site/Autodiscover" + "ECPFrontend" = "Default Web Site/ECP" + "EWSFrontend" = "Default Web Site/EWS" + "Microsoft-Server-ActiveSyncFrontend" = "Default Web Site/Microsoft-Server-ActiveSync" + "OABFrontend" = "Default Web Site/OAB" + "PowershellFrontend" = "Default Web Site/Powershell" + "OWAFrontend" = "Default Web Site/OWA" + "RPCFrontend" = "Default Web Site/RPC" + "MAPIFrontend" = "Default Web Site/MAPI" + "APIBackend" = "Exchange Back End/API" + "AutodiscoverBackend" = "Exchange Back End/Autodiscover" + "ECPBackend" = "Exchange Back End/ECP" + "EWSBackend" = "Exchange Back End/EWS" + "Microsoft-Server-ActiveSyncBackend" = "Exchange Back End/Microsoft-Server-ActiveSync" + "OABBackend" = "Exchange Back End/OAB" + "PowershellBackend" = "Exchange Back End/Powershell" + "OWABackend" = "Exchange Back End/OWA" + "RPCBackend" = "Exchange Back End/RPC" + "PushNotificationsBackend" = "Exchange Back End/PushNotifications" + "RPCWithCertBackend" = "Exchange Back End/RPCWithCert" + "MAPI-emsmdbBackend" = "Exchange Back End/MAPI/emsmdb" + "MAPI-nspiBackend" = "Exchange Back End/MAPI/nspi" + } + $Script:Logger = Get-NewLoggerInstance -LogName "ExchangeExtendedProtectionManagement-$((Get-Date).ToString("yyyyMMddhhmmss"))-Debug" ` -AppendDateTimeToFileName $false ` -ErrorAction SilentlyContinue @@ -162,6 +190,13 @@ begin { } else { $Script:SkipEWS = $false } + + if ($null -ne $RestrictType -and $RestrictType.Count -gt 0) { + $SiteVDirLocations = New-Object 'System.Collections.Generic.List[string]' + foreach ($key in $RestrictType) { + $SiteVDirLocations += $restrictionToSite[$key] + } + } } process { foreach ($server in $ExchangeServerNames) { $includeExchangeServerNames.Add($server) @@ -192,9 +227,9 @@ begin { } if ($ConfigureEPSelected) { - + $ArchivingKnownIssueString = "`r`n - Automated Archiving using Archive policy" - if($ConfigureMitigationSelected){ + if ($ConfigureMitigationSelected) { $ArchivingKnownIssueString = "" } @@ -237,7 +272,7 @@ begin { if ($ValidateMitigationSelected) { # Validate mitigation - Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -MitigationAppliedType $RestrictType + Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } if ($ShowExtendedProtection) { @@ -280,7 +315,7 @@ begin { } if ($ConfigureEPSelected) { - $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS -MitigationAppliedType $RestrictType + $prerequisitesCheck = Get-ExtendedProtectionPrerequisitesCheck -ExchangeServers $ExchangeServersPrerequisitesCheckSettingsCheck -SkipEWS $SkipEWS -SiteVDirLocations $SiteVDirLocations if ($null -ne $prerequisitesCheck) { Write-Host "" @@ -474,7 +509,7 @@ begin { if ($ConfigureMitigationSelected) { # Apply rules - Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -MitigationAppliedType $RestrictType + Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } } elseif ($RollbackSelected) { Write-Host "Prerequisite check will be skipped due to Rollback" @@ -484,10 +519,9 @@ begin { } if ($RollbackRestrictType) { - $Site = $RestrictType[0].Split("/", 2)[0] - $VDir = $RestrictType[0].Split("/", 2)[1] - Invoke-RollbackIPFiltering -ExchangeServers $ExchangeServers -Site $Site -VDir $VDir + Invoke-RollbackIPFiltering -ExchangeServers $ExchangeServers -SiteVDirLocations $SiteVDirLocations } + return } } finally { From 7b56cf07a223bd4d4017f61e33ef2b3bc113698e Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Mon, 15 Aug 2022 21:35:13 +0530 Subject: [PATCH 30/65] IPRangePathFile param name change + outputFilePath check fix --- .../ExchangeExtendedProtectionManagement.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 1df04ab8f7..ed4cb6b9cd 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -51,7 +51,7 @@ param( [Parameter (Mandatory = $false, ParameterSetName = 'GetExchangeIPs', HelpMessage = "Using this parameter will allow you to specify the path to the output file.")] [ValidateScript({ - (Test-Path -Path $_ -IsValid) -and (Test-Path -Path (Split-Path -Parent $_)) + (Test-Path -Path $_ -IsValid) -and ([string]::IsNullOrEmpty((split-path -Parent $_)) -or (Test-Path -Path (Split-Path -Parent $_))) })] [string]$OutputFilePath = [System.IO.Path]::Combine((Get-Location).Path, "IPList.txt"), @@ -60,7 +60,7 @@ param( [ValidateScript({ (Test-Path -Path $_) })] - [string]$IPRange, + [string]$IPRangeFilePath, [Parameter (Mandatory = $true, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Using this parameter will allow you to specify the site and vdir on which you want to configure mitigation.")] [ValidateSet('EWSBackend')] @@ -172,7 +172,7 @@ begin { } # Get list of IPs in object form from the file specified - $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRange + $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRangeFilePath if ($ipResults.IsError) { return } From b1ff6bebdd589baed3f6367d8dfe0cd4b7a4ca5b Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Mon, 15 Aug 2022 23:27:16 +0530 Subject: [PATCH 31/65] Minor fixes for rollback --- .../Invoke-RollbackIPFiltering.ps1 | 11 ++++++----- .../ExchangeExtendedProtectionManagement.ps1 | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 8f25b99bbc..79ec822725 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -32,6 +32,7 @@ function Invoke-RollbackIPFiltering { $SiteVDirLocations = $Arguments.SiteVDirLocations $WhatIf = $Arguments.PassedWhatIf $Filter = 'system.webServer/security/ipSecurity' + $FilterEP = 'system.WebServer/security/authentication/windowsAuthentication' $IISPath = 'IIS:\' $results = @{} @@ -71,7 +72,7 @@ function Invoke-RollbackIPFiltering { [string]$IISPath, [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, - [Parameter(Mandatory = $true)] + [Parameter(Mandatory = $false)] [object[]]$OriginalIpFilteringRules, [Parameter(Mandatory = $true)] [object]$DefaultForUnspecifiedIPs @@ -97,9 +98,9 @@ function Invoke-RollbackIPFiltering { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation ) - $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name tokenChecking + $ExtendedProtection = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name "extendedProtection.tokenChecking" if ($ExtendedProtection -ne "Require") { - Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name tokenChecking -Value "Require" + Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "extendedProtection.tokenChecking" -Value "Require" } } @@ -120,7 +121,7 @@ function Invoke-RollbackIPFiltering { } $state.RestoreFileExists = $true - TurnONEP -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation + TurnONEP -Filter $FilterEP -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation $state.TurnOnEPSuccessful = $true $state.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" @@ -128,7 +129,7 @@ function Invoke-RollbackIPFiltering { $state.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $state.BackUpPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules $originalIpFilteringConfigurations = (Get-Content $state.RestorePath | Out-String | ConvertFrom-Json) - $state.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) + $state.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation } catch { $state.ErrorContext = $_ } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index ed4cb6b9cd..87541f6e50 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -51,7 +51,7 @@ param( [Parameter (Mandatory = $false, ParameterSetName = 'GetExchangeIPs', HelpMessage = "Using this parameter will allow you to specify the path to the output file.")] [ValidateScript({ - (Test-Path -Path $_ -IsValid) -and ([string]::IsNullOrEmpty((split-path -Parent $_)) -or (Test-Path -Path (Split-Path -Parent $_))) + (Test-Path -Path $_ -IsValid) -and ([string]::IsNullOrEmpty((Split-Path -Parent $_)) -or (Test-Path -Path (Split-Path -Parent $_))) })] [string]$OutputFilePath = [System.IO.Path]::Combine((Get-Location).Path, "IPList.txt"), From 51c632ee602bee5d1a5479d30ba8530a08420433 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Tue, 16 Aug 2022 15:31:14 +0530 Subject: [PATCH 32/65] Find exchange servers IP don't consider edge server --- .../DataCollection/Get-ExchangeServerIPs.ps1 | 12 +++--------- .../ExchangeExtendedProtectionManagement.ps1 | 11 ++++++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 index bce53d36c6..4481dbff3c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -9,7 +9,9 @@ function Get-ExchangeServerIPs { [CmdletBinding()] param( [Parameter(Mandatory = $true)] - [string]$OutputFilePath + [string]$OutputFilePath, + [Parameter(Mandatory = $false)] + [object[]]$ExchangeServers ) begin { @@ -25,14 +27,6 @@ function Get-ExchangeServerIPs { Write-Verbose "Calling: $($MyInvocation.MyCommand)" } process { - try { - $ExchangeServers = Get-ExchangeServer -ErrorAction Stop - } catch { - Write-Host ("Unable to run Get-ExchangeServer due to: Inner Exception") -ForegroundColor Red - Write-HostErrorInformation $_ - return - } - $counter = 0 $totalCount = $ExchangeServers.Count diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 87541f6e50..72db797246 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -221,11 +221,6 @@ begin { return } - if ($FindExchangeServerIPAddresses) { - Get-ExchangeServerIPs -OutputFilePath $OutputFilePath - return - } - if ($ConfigureEPSelected) { $ArchivingKnownIssueString = "`r`n - Automated Archiving using Archive policy" @@ -251,6 +246,12 @@ begin { Write-Verbose ("Running Get-ExchangeServer to get list of all exchange servers") Set-ADServerSettings -ViewEntireForest $true $ExchangeServers = Get-ExchangeServer | Where-Object { $_.AdminDisplayVersion -like "Version 15*" -and $_.ServerRole -ne "Edge" } + + if ($FindExchangeServerIPAddresses) { + Get-ExchangeServerIPs -OutputFilePath $OutputFilePath -ExchangeServers $ExchangeServers + return + } + $ExchangeServersPrerequisitesCheckSettingsCheck = $ExchangeServers if ($null -ne $includeExchangeServerNames -and $includeExchangeServerNames.Count -gt 0) { From aaa6f01c96bda4b06990ec9e64a03388e8b2bfef Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Tue, 16 Aug 2022 17:50:00 +0530 Subject: [PATCH 33/65] resolved comments --- .../Invoke-ConfigureMitigation.ps1 | 6 ++-- .../Invoke-RollbackIPFiltering.ps1 | 2 +- .../Invoke-ValidateMitigation.ps1 | 2 +- .../DataCollection/Get-ExchangeServerIPs.ps1 | 3 +- .../Get-IPRangeAllowListFromFile.ps1 | 29 ++++++++++--------- 5 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index cdaa6f5d33..ad3dcfc666 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -106,9 +106,9 @@ function Invoke-ConfigureMitigation { if ($null -eq $ExistingIPSubnetRule) { if ($IpFilteringRule.Type -eq "Single IP") { - $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; } + $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; }) } else { - $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; } + $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; }) } } else { if ($ExistingIPSubnetRule.allowed -ne $IpFilteringRule.Allowed) { @@ -157,7 +157,7 @@ function Invoke-ConfigureMitigation { foreach ($localIP in $localIPs) { if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP })) { - $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } + $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$localIP; Allowed=$true }) } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 79ec822725..b72328f36f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -81,7 +81,7 @@ function Invoke-RollbackIPFiltering { Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop -WhatIf:$WhatIf $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' foreach ($IpFilteringRule in $OriginalIpFilteringRules) { - $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } + $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; }) } Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value -WhatIf:$WhatIf Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index c56aa3ff28..dafb8593e5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -147,7 +147,7 @@ function Invoke-ValidateMitigation { $localIPs = Get-LocalIpAddresses $localIPs | ForEach-Object { - $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } + $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$_; Allowed=$true }) } VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -state $state diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 index 4481dbff3c..1d41f4a38b 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -69,7 +69,8 @@ function Get-ExchangeServerIPs { $IPs | Out-File $OutputFilePath Write-Host ("Please find the collected IPs at {0}" -f $OutputFilePath) } catch { - Write-Host "Unable to write to file. Please check the path provided." -ForegroundColor Red + Write-Host "Unable to write to file. Please check the path provided. Inner Exception:" -ForegroundColor Red + Write-HostErrorInformation $_ } } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index eb43055418..ebb04e6ef8 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -26,7 +26,7 @@ function Get-IPRangeAllowListFromFile { } catch { Write-Host "Unable to read the content of file provided for IPRange. Inner Exception" -ForegroundColor Red Write-HostErrorInformation $_ - return $results + return } if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { @@ -49,11 +49,11 @@ function Get-IPRangeAllowListFromFile { # Check the type of IP address (IPv4/IPv6) $IpAddress = $IpAddressString -as [IPAddress] - + $baseError = "Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets." if ($null -eq $IpAddress -or $null -eq $IpAddress.AddressFamily) { # Invalid IP address found - Write-Host ("Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Rexecute the command with proper input file for IPRange parameter. Invalid IP address detected: {0}." -f $IpAddressString) -ForegroundColor Red - return $results + Write-Host ("$baseError Re-execute the command with proper input file for IPRange parameter. Invalid IP address detected: {0}." -f $IpAddressString) -ForegroundColor Red + return } $IsIPv6 = $IpAddress.AddressFamily -eq [System.Net.Sockets.AddressFamily]::InterNetworkV6 @@ -62,25 +62,25 @@ function Get-IPRangeAllowListFromFile { # Check if the subnet value is valid (IPv4 <= 32, IPv6 <= 128 or empty) $SubnetMask = $SubnetMaskString -as [int] - $InvalidSubnetMaskString = "Input file provided for IPRange doesn't have correct syntax of IPs or IP subnets. Invalid Subnet Mask found: The Subnet Mask {0} is not in valid range.Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128. Re-execute the command with proper input file for IPRange parameter." + $InvalidSubnetMaskString = "$baseError Invalid Subnet Mask found: The Subnet Mask $SubnetMaskString is not in valid range.Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128. Re-execute the command with proper input file for IPRange parameter." if ($null -eq $SubnetMask) { - Write-Host ($InvalidSubnetMaskString -f $SubnetMaskString) -ForegroundColor Red + Write-Host ($InvalidSubnetMaskString) -ForegroundColor Red $results.IsError = $true - return $results + return } elseif (($SubnetMask -gt 32 -and -not $IsIPv6) -or $SubnetMask -gt 128 -or $SubnetMask -lt 0) { - Write-Host ($InvalidSubnetMaskString -f $SubnetMaskString) -ForegroundColor Red + Write-Host ($InvalidSubnetMaskString) -ForegroundColor Red $results.IsError = $true - return $results + return } if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Subnet" -and $_.IP -eq $IpAddressString -and $_.SubnetMask -eq $SubnetMaskString })) { - $results.ipRangeAllowListRules += @{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true } + $results.ipRangeAllowListRules.Add(@{Type = "Subnet"; IP=$IpAddressString; SubnetMask=$SubnetMaskString; Allowed=$true }) } else { Write-Verbose ("Not adding $IpAddressString/$SubnetMaskString to the list as it is a duplicate entry in the file provided.") } } else { if ($null -eq ($results.ipRangeAllowListRules | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $IpAddressString })) { - $results.ipRangeAllowListRules += @{Type = "Single IP"; IP=$IpAddressString; Allowed=$true } + $results.ipRangeAllowListRules.Add(@{Type = "Single IP"; IP=$IpAddressString; Allowed=$true }) } else { Write-Verbose ("Not adding $IpAddressString to the list as it is a duplicate entry in the file provided.") } @@ -89,16 +89,17 @@ function Get-IPRangeAllowListFromFile { if ($results.ipRangeAllowListRules.count -gt 500) { Write-Host ("Too many IP filtering rules. Please reduce the specified entries by providing appropriate subnets." -f $SubnetMaskString) -ForegroundColor Red - return $results + return } } catch { Write-Host ("Unable to create IP allow rules. Inner Exception") -ForegroundColor Red Write-HostErrorInformation $_ - return $results + return } + + $results.IsError = $false } end { - $results.IsError = $false return $results } } From e0e95ba76b939334622bba8025f51b14db1ea9f5 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Tue, 16 Aug 2022 21:58:44 +0530 Subject: [PATCH 34/65] fixed minor issues --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 8 ++++---- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 6 +++--- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 6 +++--- .../ExchangeExtendedProtectionManagement.ps1 | 8 ++++---- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index ad3dcfc666..9247fb72d3 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -11,7 +11,7 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, [Parameter(Mandatory = $true)] - [object[]]$ipRangeAllowListRules, + [System.Collections.Generic.List[object]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] [string[]]$SiteVDirLocations ) @@ -54,7 +54,7 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $false)] - [object[]]$ExistingRules + [System.Collections.Generic.List[object]]$ExistingRules ) $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" @@ -88,7 +88,7 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $true)] - [object[]]$IpFilteringRules, + [System.Collections.Generic.List[object]]$IpFilteringRules, [Parameter(Mandatory = $true)] [hashtable] $state ) @@ -157,7 +157,7 @@ function Invoke-ConfigureMitigation { foreach ($localIP in $localIPs) { if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP })) { - $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$localIP; Allowed=$true }) + $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$localIP; Allowed=$true }) > $null } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index b72328f36f..e35653b180 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -48,7 +48,7 @@ function Invoke-RollbackIPFiltering { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $false)] - [object[]]$ExistingRules + [System.Collections.Generic.List[object]]$ExistingRules ) $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" @@ -73,7 +73,7 @@ function Invoke-RollbackIPFiltering { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $false)] - [object[]]$OriginalIpFilteringRules, + [System.Collections.Generic.List[object]]$OriginalIpFilteringRules, [Parameter(Mandatory = $true)] [object]$DefaultForUnspecifiedIPs ) @@ -81,7 +81,7 @@ function Invoke-RollbackIPFiltering { Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop -WhatIf:$WhatIf $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' foreach ($IpFilteringRule in $OriginalIpFilteringRules) { - $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; }) + $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; }) > $null } Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value -WhatIf:$WhatIf Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index dafb8593e5..da8dc89122 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -11,7 +11,7 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, [Parameter(Mandatory = $false)] - [object[]]$ipRangeAllowListRules, + [System.Collections.Generic.List[object]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] [string[]]$SiteVDirLocations ) @@ -85,7 +85,7 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $true)] - [object[]]$IpFilteringRules, + [System.Collections.Generic.List[object]]$IpFilteringRules, [Parameter(Mandatory = $true)] [hashtable]$state ) @@ -147,7 +147,7 @@ function Invoke-ValidateMitigation { $localIPs = Get-LocalIpAddresses $localIPs | ForEach-Object { - $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$_; Allowed=$true }) + $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$_; Allowed=$true }) > $null } VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -state $state diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 21071ca5ce..be99c38ee8 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -148,7 +148,7 @@ begin { if ($RollbackType.Contains("RestoreIISAppConfig")) { if ($RollbackType.Length -gt 1) { Write-Host "RestoreIISAppConfig Rollback type can only be used individually" - return + exit } $RollbackRestoreIISAppConfig = $true @@ -174,7 +174,7 @@ begin { # Get list of IPs in object form from the file specified $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRangeFilePath if ($ipResults.IsError) { - return + exit } $ipRangeAllowListRules = $ipResults.ipRangeAllowListRules @@ -218,7 +218,7 @@ begin { if ((Test-ScriptVersion -AutoUpdate -VersionsUrl "https://aka.ms/CEP-VersionsUrl")) { Write-Warning "Script was updated. Please rerun the command." - return + exit } if ($ConfigureEPSelected) { @@ -268,7 +268,7 @@ begin { if ($null -eq $ExchangeServers) { Write-Host "No exchange servers to process. Please specify server filters correctly" - return + exit } if ($ValidateMitigationSelected) { From cd5dcbbd8028ce2740bf85c497553f0a7b60eef4 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 00:39:24 +0530 Subject: [PATCH 35/65] Fix issue when rolling back 1 ip --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 2 +- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 2 +- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 9247fb72d3..d608cac52b 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -96,7 +96,7 @@ function Invoke-ConfigureMitigation { $backupPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) $state.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index e35653b180..18a18bb453 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -125,7 +125,7 @@ function Invoke-RollbackIPFiltering { $state.TurnOnEPSuccessful = $true $state.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) $state.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $state.BackUpPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules $originalIpFilteringConfigurations = (Get-Content $state.RestorePath | Out-String | ConvertFrom-Json) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index da8dc89122..f0159ca633 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -100,7 +100,7 @@ function Invoke-ValidateMitigation { $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' - $ExistingRules = Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection + $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) foreach ($IpFilteringRule in $IpFilteringRules) { $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") -and $_.allowed -eq $IpFilteringRule.Allowed } From 140088c2e963f1ce97f9e45a099f518db3538197 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 00:54:49 +0530 Subject: [PATCH 36/65] minor fix --- .../Invoke-ConfigureMitigation.ps1 | 7 +++++-- .../Invoke-ValidateMitigation.ps1 | 13 ++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 9247fb72d3..b6d99891ef 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -129,7 +129,7 @@ function Invoke-ConfigureMitigation { } if ($RulesToBeAdded.Count -gt 0) { - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded.ToArray() -ErrorAction Stop -WhatIf:$WhatIf } $state.IsCreateIPRulesSuccessful = $true @@ -215,6 +215,10 @@ function Invoke-ConfigureMitigation { $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) } + $SiteVDirLocations | ForEach-Object { + $FailedServersFilter[$_] = New-Object 'System.Collections.Generic.List[string]' + } + foreach ($Server in $ExchangeServers) { $baseStatus = "Processing: $Server -" $progressParams.PercentComplete = ($counter / $totalCount * 100) @@ -251,7 +255,6 @@ function Invoke-ConfigureMitigation { foreach ($SiteVDirLocation in $SiteVDirLocations) { $state = $resultsInvoke[$SiteVDirLocation] - $FailedServersFilter[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' if ($state.IsBackUpSuccessful) { Write-Verbose ("Successfully backed up IP filtering allow list for VDir $SiteVDirLocation") diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index da8dc89122..a8ee55a3ea 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -175,6 +175,14 @@ function Invoke-ValidateMitigation { $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) } + $SiteVDirLocations | ForEach-Object { + $FailedServersEP[$_] = New-Object 'System.Collections.Generic.List[string]' + $FailedServersFilter[$_] = New-Object 'System.Collections.Generic.List[string]' + + $UnMitigatedServersEP[$_] = New-Object 'System.Collections.Generic.List[string]' + $UnMitigatedServersFilter[$_] = New-Object 'System.Collections.Generic.List[string]' + } + foreach ($Server in $ExchangeServers) { $baseStatus = "Processing: $Server -" $progressParams.PercentComplete = ($counter / $totalCount * 100) @@ -186,11 +194,6 @@ function Invoke-ValidateMitigation { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs foreach ($SiteVDirLocation in $SiteVDirLocations) { $state = $resultsInvoke[$SiteVDirLocation] - $FailedServersEP[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' - $FailedServersFilter[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' - - $UnMitigatedServersEP[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' - $UnMitigatedServersFilter[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' if ($state.IsEPOff) { Write-Verbose ("Expected: The state of Extended protection flag is None for Vdir: $($SiteVDirLocation)") From d40726cc5f38b76668123ae0bf9e885a330488b8 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 01:30:01 +0530 Subject: [PATCH 37/65] reverted list to array --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 10 +++++----- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 4 ++-- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index aaf3d69ec9..e8f5c130ea 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -11,7 +11,7 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, [Parameter(Mandatory = $true)] - [System.Collections.Generic.List[object]]$ipRangeAllowListRules, + [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] [string[]]$SiteVDirLocations ) @@ -54,7 +54,7 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $false)] - [System.Collections.Generic.List[object]]$ExistingRules + [object[]]$ExistingRules ) $DefaultForUnspecifiedIPs = Get-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" @@ -88,7 +88,7 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $true)] - [System.Collections.Generic.List[object]]$IpFilteringRules, + [object[]]$IpFilteringRules, [Parameter(Mandatory = $true)] [hashtable] $state ) @@ -129,7 +129,7 @@ function Invoke-ConfigureMitigation { } if ($RulesToBeAdded.Count -gt 0) { - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded.ToArray() -ErrorAction Stop -WhatIf:$WhatIf + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf } $state.IsCreateIPRulesSuccessful = $true @@ -157,7 +157,7 @@ function Invoke-ConfigureMitigation { foreach ($localIP in $localIPs) { if ($null -eq ($IpRangesForFiltering | Where-Object { $_.Type -eq "Single IP" -and $_.IP -eq $localIP })) { - $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$localIP; Allowed=$true }) > $null + $IpRangesForFiltering += @{Type="Single IP"; IP=$localIP; Allowed=$true } } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 18a18bb453..34b22f45c2 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -73,7 +73,7 @@ function Invoke-RollbackIPFiltering { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $false)] - [System.Collections.Generic.List[object]]$OriginalIpFilteringRules, + [object[]]$OriginalIpFilteringRules, [Parameter(Mandatory = $true)] [object]$DefaultForUnspecifiedIPs ) @@ -81,7 +81,7 @@ function Invoke-RollbackIPFiltering { Clear-WebConfiguration -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -ErrorAction Stop -WhatIf:$WhatIf $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' foreach ($IpFilteringRule in $OriginalIpFilteringRules) { - $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; }) > $null + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } } Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value -WhatIf:$WhatIf Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index e2e9147bbd..a0e05cc8c4 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -11,7 +11,7 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, [Parameter(Mandatory = $false)] - [System.Collections.Generic.List[object]]$ipRangeAllowListRules, + [object[]]$ipRangeAllowListRules, [Parameter(Mandatory = $true)] [string[]]$SiteVDirLocations ) @@ -85,7 +85,7 @@ function Invoke-ValidateMitigation { [Parameter(Mandatory = $true)] [string]$SiteVDirLocation, [Parameter(Mandatory = $true)] - [System.Collections.Generic.List[object]]$IpFilteringRules, + [object[]]$IpFilteringRules, [Parameter(Mandatory = $true)] [hashtable]$state ) @@ -147,7 +147,7 @@ function Invoke-ValidateMitigation { $localIPs = Get-LocalIpAddresses $localIPs | ForEach-Object { - $IpRangesForFiltering.Add(@{Type="Single IP"; IP=$_; Allowed=$true }) > $null + $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } } VerifyIPRangeAllowList -SiteVDirLocation $SiteVDirLocation -IpFilteringRules $IpRangesForFiltering -state $state From 75e9dfa4852aa800f7f9959a131fcbf84a93b855 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 02:16:35 +0530 Subject: [PATCH 38/65] minor issue fixed. unable to add ips to iis --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index e8f5c130ea..34ae51637b 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -99,16 +99,16 @@ function Invoke-ConfigureMitigation { $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) $state.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules - $RulesToBeAdded = New-Object 'System.Collections.Generic.List[object]' + $RulesToBeAdded = @() foreach ($IpFilteringRule in $IpFilteringRules) { $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } if ($null -eq $ExistingIPSubnetRule) { if ($IpFilteringRule.Type -eq "Single IP") { - $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; }) + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; allowed=$IpFilteringRule.Allowed; } } else { - $RulesToBeAdded.Add(@{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; }) + $RulesToBeAdded += @{ipAddress=$IpFilteringRule.IP; subnetMask=$IpFilteringRule.SubnetMask; allowed=$IpFilteringRule.Allowed; } } } else { if ($ExistingIPSubnetRule.allowed -ne $IpFilteringRule.Allowed) { From 129da404ef05d25f2c4d90b791d415e96d1ca8dd Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 02:31:55 +0530 Subject: [PATCH 39/65] Fixing rollback issue when we need to rollback to no rules state --- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 34b22f45c2..d00f229d3c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -84,7 +84,9 @@ function Invoke-RollbackIPFiltering { $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } } Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value -WhatIf:$WhatIf - Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf + if($OriginalIpFilteringRules.Length -gt 0){ + Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf + } return $true } From 3ea769bc0ea56edd02884858bffeb894d3eb668d Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 02:51:13 +0530 Subject: [PATCH 40/65] resolved the $ipRangeAllowListString print issue --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 34ae51637b..31655af12a 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -212,7 +212,15 @@ function Invoke-ConfigureMitigation { if ($null -eq $ipRangeAllowListRules) { $ipRangeAllowListString = "null" } else { - $ipRangeAllowListString = [string]::Join(", ", $ipRangeAllowListRules) + $IpStrings = @() + $ipRangeAllowListRules | ForEach-Object { + if ($_.Type -eq "Single IP") { + $IpStrings += $_.IP + } else { + $IpStrings += ("{0}/{1}" -f $_.IP, $_.SubnetMask) + } + } + $ipRangeAllowListString = [string]::Join(", ", $IpStrings) } $SiteVDirLocations | ForEach-Object { From c94b7cbc250075e93d5126f5499517c19bdb7e6a Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 02:56:03 +0530 Subject: [PATCH 41/65] Fixing formatting --- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index d00f229d3c..2c32706beb 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -84,7 +84,7 @@ function Invoke-RollbackIPFiltering { $RulesToBeAdded += @{ipAddress=$IpFilteringRule.ipAddress; subnetMask=$IpFilteringRule.subnetMask; domainName=$IpFilteringRule.domainName; allowed=$IpFilteringRule.allowed; } } Set-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "allowUnlisted" -Value $DefaultForUnspecifiedIPs.Value -WhatIf:$WhatIf - if($OriginalIpFilteringRules.Length -gt 0){ + if ($OriginalIpFilteringRules.Length -gt 0) { Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf } From 0c00d0c09da2008a55ce151533c4c513ddecb980 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 03:00:19 +0530 Subject: [PATCH 42/65] Fixing formatting --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 31655af12a..861be70757 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -212,7 +212,7 @@ function Invoke-ConfigureMitigation { if ($null -eq $ipRangeAllowListRules) { $ipRangeAllowListString = "null" } else { - $IpStrings = @() + $IpStrings = @() $ipRangeAllowListRules | ForEach-Object { if ($_.Type -eq "Single IP") { $IpStrings += $_.IP From d8988a6245c0d7430a142a6de39b2116e9e03616 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Tue, 16 Aug 2022 16:32:03 -0500 Subject: [PATCH 43/65] Simplify $PsCmdlet.ParameterSetName being used --- .../ExchangeExtendedProtectionManagement.ps1 | 55 ++++++++----------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index be99c38ee8..0f97797505 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -134,43 +134,36 @@ begin { SetWriteHostAction ${Function:Write-HostLog} - $RollbackSelected = $false - $RollbackRestrictType = $false - $ConfigureEPSelected = $false - $ConfigureMitigationSelected = $false - $ValidateMitigationSelected = $false - $Script:SkipEWS = $false + # The ParameterSetName options + $RollbackSelected = $PsCmdlet.ParameterSetName -eq "Rollback" + $RollbackRestoreIISAppConfig = $RollbackType.Contains("RestoreIISAppConfig") + $RollbackRestrictType = $RollbackSelected -and (-not $RollbackRestoreIISAppConfig) + $ConfigureMitigationSelected = $PsCmdlet.ParameterSetName -eq "ConfigureMitigation" + $ConfigureEPSelected = $ConfigureMitigationSelected -or + ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) + $ValidateMitigationSelected = $PsCmdlet.ParameterSetName -eq "ValidateMitigation" $includeExchangeServerNames = New-Object 'System.Collections.Generic.List[string]' - if ($PsCmdlet.ParameterSetName -eq "Rollback") { - $RollbackSelected = $true - if ($RollbackType.Contains("RestoreIISAppConfig")) { - if ($RollbackType.Length -gt 1) { - Write-Host "RestoreIISAppConfig Rollback type can only be used individually" - exit - } + if ($RollbackRestoreIISAppConfig -and $RollbackType.Length -gt 1) { + Write-Host "RestoreIISAppConfig Rollback type can only be used individually" + exit + } - $RollbackRestoreIISAppConfig = $true - } else { - $RollbackRestrictType = $true - $RestrictType = $RollbackType.Replace("RestrictType", "") - } + if ($RollbackRestrictType) { + $RestrictType = $RollbackType.Replace("RestrictType", "") } - if (($PsCmdlet.ParameterSetName -eq "ConfigureMitigation" -or $PsCmdlet.ParameterSetName -eq "ValidateMitigation")) { - if ($PsCmdlet.ParameterSetName -eq "ConfigureMitigation") { - $ConfigureEPSelected = $true - $ConfigureMitigationSelected = $true - $RestrictType = $RestrictType | Get-Unique - } + if ($ConfigureMitigationSelected) { + $RestrictType = $RestrictType | Get-Unique + } - if ($PsCmdlet.ParameterSetName -eq "ValidateMitigation") { - $ValidateMitigationSelected = $true - $RestrictType = New-Object 'System.Collections.Generic.List[string]' - $ValidateMitigation | Get-Unique | ForEach-Object { $RestrictType += $_.Replace("RestrictType", "") } - } + if ($ValidateMitigationSelected) { + $RestrictType = New-Object 'System.Collections.Generic.List[string]' + $ValidateMitigation | Get-Unique | ForEach-Object { $RestrictType += $_.Replace("RestrictType", "") } + } + if (($ConfigureMitigationSelected -or $ValidateMitigationSelected)) { # Get list of IPs in object form from the file specified $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRangeFilePath if ($ipResults.IsError) { @@ -180,10 +173,6 @@ begin { $ipRangeAllowListRules = $ipResults.ipRangeAllowListRules } - if ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) { - $ConfigureEPSelected = $true - } - if ($InternalOption -eq "SkipEWS") { Write-Verbose "SkipEWS option enabled." $Script:SkipEWS = $true From 5ec5ab5db39e45aa7027b036a3eeea5fa9c13b09 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 03:30:19 +0530 Subject: [PATCH 44/65] Resolved parameter set issue for $ShowExtendedProtection parameter --- .../ExchangeExtendedProtectionManagement.ps1 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 0f97797505..085abd881a 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -39,8 +39,7 @@ param( [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Enter the list of servers on which the script should not execute on")] [string[]]$SkipExchangeServerNames = $null, - [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] - [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureMitigation', HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] + [Parameter (Mandatory = $true, ParameterSetName = 'ShowEP', HelpMessage = "Enable to provide a result of the configuration for Extended Protection")] [switch]$ShowExtendedProtection, [Parameter (Mandatory = $false, ParameterSetName = 'ConfigureEP', HelpMessage = "Used for internal options")] @@ -270,11 +269,10 @@ begin { $extendedProtectionConfigurations = New-Object 'System.Collections.Generic.List[object]' foreach ($server in $ExchangeServers) { $params = @{ - ComputerName = $server.ToString() - IsClientAccessServer = $server.IsClientAccessServer - IsMailboxServer = $server.IsMailboxServer - ExcludeEWS = $SkipEWS - MitigationAppliedType = $RestrictType + ComputerName = $server.ToString() + IsClientAccessServer = $server.IsClientAccessServer + IsMailboxServer = $server.IsMailboxServer + ExcludeEWS = $SkipEWS } $extendedProtectionConfigurations.Add((Get-ExtendedProtectionConfiguration @params)) } From fef569d5ee1d05f5f7047d4c527b42c67daa7a9a Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 03:42:37 +0530 Subject: [PATCH 45/65] Fix - change rollbackType to array from string --- .../ExchangeExtendedProtectionManagement.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index be99c38ee8..2fb233c20b 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -78,7 +78,7 @@ param( [Parameter (Mandatory = $true, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [ValidateSet('RestrictTypeEWSBackend', 'RestoreIISAppConfig')] - [string]$RollbackType + [string[]]$RollbackType ) begin { From 43713373ec8972a26477c5a7917e7484482328f7 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 04:09:53 +0530 Subject: [PATCH 46/65] Rollback Unreachable server fix --- .../Invoke-RollbackIPFiltering.ps1 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 2c32706beb..6ff07954b6 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -149,6 +149,11 @@ function Invoke-RollbackIPFiltering { $exchangeServersProcessed = 0 $totalExchangeServers = $ExchangeServers.Count + + $SiteVDirLocations | ForEach-Object { + $FailedServers[$_] = New-Object 'System.Collections.Generic.List[string]' + } + foreach ($Server in $ExchangeServers) { $baseStatus = "Processing: $($Server.Name) -" $progressParams.PercentComplete = ($exchangeServersProcessed / $totalExchangeServers * 100) @@ -160,10 +165,17 @@ function Invoke-RollbackIPFiltering { Write-Verbose ("Restoring previous state for Server {0}" -f $Server.Name) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs + if ($null -eq $resultsInvoke) { + $line = "Failed to restore application host config file on server $($Server.Name), because we weren't able to reach it." + Write-Verbose $line + Write-Warning $line + $SiteVDirLocations | ForEach-Object { $FailedServers[$_].Add($Server.Name) } + continue + } + foreach ($SiteVDirLocation in $SiteVDirLocations) { $Failed = $false $state = $resultsInvoke[$SiteVDirLocation] - $FailedServers[$SiteVDirLocation] = New-Object 'System.Collections.Generic.List[string]' if ($state.RestoreFileExists) { if ($state.TurnOnEPSuccessful) { Write-Host "Turned on EP on server $($Server.Name) for VDir $SiteVDirLocation" @@ -199,7 +211,7 @@ function Invoke-RollbackIPFiltering { } end { foreach ($SiteVDirLocation in $SiteVDirLocations) { if ($FailedServers[$SiteVDirLocation].Length -gt 0) { - Write-Host ("Unable to rollback for VDir $SiteVDirLocation on the following servers: {0}" -f [string]::Join(", ", $FailedServers)) -ForegroundColor Red + Write-Host ("Unable to rollback for VDir $SiteVDirLocation on the following servers: {0}" -f [string]::Join(", ", $FailedServers[$SiteVDirLocation])) -ForegroundColor Red } } } From 4fcb868d8216b8514f64e279a1101a6add205362 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 04:25:06 +0530 Subject: [PATCH 47/65] Fixing connection issues in validate-mitigation --- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 2 +- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 6ff07954b6..9a7568d202 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -166,7 +166,7 @@ function Invoke-RollbackIPFiltering { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Failed to restore application host config file on server $($Server.Name), because we weren't able to reach it." + $line = "Failed to rollback ip filtering rules on server $($Server.Name), because we weren't able to reach it." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServers[$_].Add($Server.Name) } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index a0e05cc8c4..e102d19be9 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -192,6 +192,16 @@ function Invoke-ValidateMitigation { Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocations: {1}, ipRangeAllowListRules: {2}" -f $Server, [string]::Join(", ", $SiteVDirLocations), $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs + + if ($null -eq $resultsInvoke) { + $line = "Failed to validate ip filtering rules on server $($Server), because we weren't able to reach it." + Write-Verbose $line + Write-Warning $line + $SiteVDirLocations | ForEach-Object { $FailedServersEP[$_].Add($Server) } + $SiteVDirLocations | ForEach-Object { $FailedServersFilter[$_].Add($Server) } + continue + } + foreach ($SiteVDirLocation in $SiteVDirLocations) { $state = $resultsInvoke[$SiteVDirLocation] From f0c1361f45148f7bb7d8b94848648aa80e168427 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 04:36:15 +0530 Subject: [PATCH 48/65] RollbackType param fix --- .../ExchangeExtendedProtectionManagement.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index aad4bd8eb5..d42332ac6f 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -135,7 +135,7 @@ begin { # The ParameterSetName options $RollbackSelected = $PsCmdlet.ParameterSetName -eq "Rollback" - $RollbackRestoreIISAppConfig = $RollbackType.Contains("RestoreIISAppConfig") + $RollbackRestoreIISAppConfig = $RollbackSelected -and $RollbackType.Contains("RestoreIISAppConfig") $RollbackRestrictType = $RollbackSelected -and (-not $RollbackRestoreIISAppConfig) $ConfigureMitigationSelected = $PsCmdlet.ParameterSetName -eq "ConfigureMitigation" $ConfigureEPSelected = $ConfigureMitigationSelected -or From ee6712adaae390867737bb2cdd0009bd828350aa Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 11:32:44 +0530 Subject: [PATCH 49/65] Fixing failure case for get-exchangeServerIps --- .../DataCollection/Get-ExchangeServerIPs.ps1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 index 1d41f4a38b..8c38353642 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -36,23 +36,26 @@ function Get-ExchangeServerIPs { $progressParams.Status = "$baseStatus Getting IPs" Write-Progress @progressParams + $IpsFound = $false $HostNetworkInfo = Get-AllNicInformation -ComputerName $Server.Name -ComputerFQDN $Server.FQDN if ($null -ne $HostNetworkInfo) { if ($null -ne $HostNetworkInfo.IPv4Addresses) { foreach ($address in $HostNetworkInfo.IPv4Addresses) { $IPs += $address.Address + $IpsFound = $true } } if ($null -ne $HostNetworkInfo.IPv6Addresses) { foreach ($address in $HostNetworkInfo.IPv6Addresses) { $IPs += $address.Address + $IpsFound = $true } } } - if ($IPs.Length -eq 0) { + if (-not $IpsFound) { $FailedServers += $Server.Name - Write-Verbose "Ip of $($Server.Name) cannot be found and will not be added to ip allow list." -ForegroundColor Red + Write-Verbose "Ip of $($Server.Name) cannot be found and will not be added to ip allow list." } $counter++ From c8ff1015c9ed9248b74da2e8bd74b21187240e12 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 13:29:00 +0530 Subject: [PATCH 50/65] made minor logging changes --- .../Invoke-ConfigureMitigation.ps1 | 20 ++++++------- .../Invoke-RollbackIPFiltering.ps1 | 6 ++-- .../Invoke-ValidateMitigation.ps1 | 28 +++++++++---------- .../DataCollection/Get-ExchangeServerIPs.ps1 | 2 +- .../Get-IPRangeAllowListFromFile.ps1 | 2 +- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 861be70757..d02cae20a5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -82,7 +82,7 @@ function Invoke-ConfigureMitigation { return $ips } - # Create ip allow list from user provided ip subnets + # Create IP allow list from user provided IP subnets function CreateIPRangeAllowList { param ( [Parameter(Mandatory = $true)] @@ -237,7 +237,7 @@ function Invoke-ConfigureMitigation { Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs - Write-Host ("Adding IP Restriction rules on Server {0}" -f $Server) + Write-Verbose ("Adding IP Restriction rules on Server {0}" -f $Server) if ($resultsInvoke.IsWindowsFeatureInstalled) { Write-Verbose ("Successfully installed windows feature - Web-IP-Security on server {0}" -f $Server) } else { @@ -265,39 +265,39 @@ function Invoke-ConfigureMitigation { $state = $resultsInvoke[$SiteVDirLocation] if ($state.IsBackUpSuccessful) { - Write-Verbose ("Successfully backed up IP filtering allow list for VDir $SiteVDirLocation") + Write-Verbose ("Successfully backed up IP filtering allow list for VDir $SiteVDirLocation on server $Server") } else { - Write-Host ("Script failed to backup IP filtering allow list for VDir $SiteVDirLocation with the Inner Exception:") -ForegroundColor Red + Write-Host ("Script failed to backup IP filtering allow list for VDir $SiteVDirLocation on server $Server with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $state.ErrorContext $FailedServersFilter[$SiteVDirLocation] += $Server continue } if ($state.IsCreateIPRulesSuccessful) { - Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation") + Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation on server $Server") if ($state.IPsNotAdded.Length -gt 0) { - $line = ("Few IPs were not added to the allow list as deny rules for these IPs were already present.") + $line = ("Some IPs provided in the IPRange file were present in deny rules, hence these IPs were not added in the Allow List for VDir $SiteVDirLocation on server $Server. If you wish to add these IPs in allow list, remove these IPs from deny list in module name and reapply IP restrictions again.") Write-Warning ($line + "Check logs for further details.") Write-Verbose $line Write-Verbose (GetCommaSaperatedString -list $state.IPsNotAdded) } } else { - Write-Host ("Script failed to update IP filtering allow list for VDir $SiteVDirLocation. with the Inner Exception:") -ForegroundColor Red + Write-Host ("Script failed to update IP filtering allow list for VDir $SiteVDirLocation on server $Server with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $state.ErrorContext $FailedServersFilter[$SiteVDirLocation] += $Server continue } if ($state.IsSetDefaultRuleSuccessful) { - Write-Verbose ("Successfully set the default IP filtering rule to deny for VDir $SiteVDirLocation") + Write-Verbose ("Successfully set the default IP filtering rule to deny for VDir $SiteVDirLocation on server $Server") } else { - Write-Host ("Script failed to set the default IP filtering rule to deny for VDir $SiteVDirLocation with the Inner Exception:") -ForegroundColor Red + Write-Host ("Script failed to set the default IP filtering rule to deny for VDir $SiteVDirLocation on server $Server with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $state.ErrorContext $FailedServersFilter[$SiteVDirLocation] += $Server continue } - Write-Host ("Enabled ip filtering rules on server {0} for VDir $SiteVDirLocation" -f $Server) + Write-Host ("Enabled IP filtering rules on server {0} for VDir $SiteVDirLocation" -f $Server) } } } end { diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 9a7568d202..e5379883f7 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -166,7 +166,7 @@ function Invoke-RollbackIPFiltering { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Failed to rollback ip filtering rules on server $($Server.Name), because we weren't able to reach it." + $line = "Failed to rollback IP filtering rules on server $($Server.Name), because we weren't able to reach it." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServers[$_].Add($Server.Name) } @@ -182,9 +182,9 @@ function Invoke-RollbackIPFiltering { if ($state.BackupCurrentSuccessful) { Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($state.BackUpPath) for VDir $SiteVDirLocation" if ($state.RestoreSuccessful) { - Write-Host "Successfully rolled back ip filtering rules on server $($Server.Name) from $($state.RestorePath) for VDir $SiteVDirLocation" + Write-Host "Successfully rolled back IP filtering rules on server $($Server.Name) from $($state.RestorePath) for VDir $SiteVDirLocation" } else { - Write-Host "Failed to rollback ip filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red + Write-Host "Failed to rollback IP filtering rules on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red Write-HostErrorInformation $state.ErrorContext $Failed = $true } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index e102d19be9..85beb0a078 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -79,7 +79,7 @@ function Invoke-ValidateMitigation { return $ExtendedProtection } - # Create ip allow list from user provided ip subnets + # Create IP allow list from user provided IP subnets function VerifyIPRangeAllowList { param ( [Parameter(Mandatory = $true)] @@ -194,7 +194,7 @@ function Invoke-ValidateMitigation { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Failed to validate ip filtering rules on server $($Server), because we weren't able to reach it." + $line = "Failed to validate IP filtering rules on server $($Server), because we weren't able to reach it." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServersEP[$_].Add($Server) } @@ -206,12 +206,12 @@ function Invoke-ValidateMitigation { $state = $resultsInvoke[$SiteVDirLocation] if ($state.IsEPOff) { - Write-Verbose ("Expected: The state of Extended protection flag is None for Vdir: $($SiteVDirLocation)") + Write-Verbose ("Expected: The state of Extended protection flag is None for Vdir $($SiteVDirLocation) on server $Server") } elseif ($state.IsEPVerified) { - Write-Host ("Unexpected: The state of Extended protection flag is not set to None for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + Write-Host ("Unexpected: The state of Extended protection flag is not set to None for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red $UnMitigatedServersEP[$SiteVDirLocation] += $Server } else { - Write-Host ("Unknown: Script failed to get state of Extended protection flag for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-Host ("Unknown: Script failed to get state of Extended protection flag for Vdir $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext $FailedServersEP[$SiteVDirLocation] += $Server $FailedServersFilter[$SiteVDirLocation] += $Server @@ -221,35 +221,35 @@ function Invoke-ValidateMitigation { $IsFilterUnMitigated = $false if (-not $state.IsWindowsFeatureVerified) { - Write-Host ("Unknown: Script failed to verify if the Windows feature Web-IP-Security is present for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-Host ("Unknown: Script failed to verify if the Windows feature Web-IP-Security is present for Vdir $($SiteVDirLocation) on server $Server with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext $FailedServersFilter[$SiteVDirLocation] += $Server continue } elseif (-not $state.IsWindowsFeatureInstalled) { - Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red $IsFilterUnMitigated = $true } else { - Write-Verbose ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server for Vdir: $($SiteVDirLocation)") + Write-Verbose ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server for Vdir $($SiteVDirLocation) on server $Server") if (-not $state.AreIPRulesVerified) { - Write-Host ("Unknown: Script failed to verify IP Filtering Rules for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-Host ("Unknown: Script failed to verify IP Filtering Rules for Vdir $($SiteVDirLocation) on server $Server with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext $FailedServersFilter[$SiteVDirLocation] += $Server continue } elseif ($null -ne $state.RulesNotFound -and $state.RulesNotFound.Length -gt 0) { - Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $state.RulesNotFound)) $IsFilterUnMitigated = $true } else { - Write-Verbose ("Expected: Successfully verified all the IP filtering rules for Vdir: $($SiteVDirLocation)") + Write-Verbose ("Expected: Successfully verified all the IP filtering rules for Vdir $($SiteVDirLocation) on server $Server") } if ($state.IsDefaultFilterDeny) { - Write-Verbose ("Expected: The default IP Filtering rule is set to deny for Vdir: $($SiteVDirLocation)") + Write-Verbose ("Expected: The default IP Filtering rule is set to deny for Vdir $($SiteVDirLocation) on server $Server") } elseif ($state.IsDefaultFilterVerified) { - Write-Host ("Unexpected: The default IP Filtering rule is not set to deny for Vdir: $($SiteVDirLocation)") -ForegroundColor Red + Write-Host ("Unexpected: The default IP Filtering rule is not set to deny for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red $IsFilterUnMitigated = $true } else { - Write-Host ("Unknown: Script failed to get the default IP Filtering rule for Vdir: $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red + Write-Host ("Unknown: Script failed to get the default IP Filtering rule for Vdir $($SiteVDirLocation) on server $Server with Inner Exception") -ForegroundColor Red Write-HostErrorInformation $results.ErrorContext $FailedServersFilter[$SiteVDirLocation] += $Server continue diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 index 8c38353642..a31fd3ee43 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-ExchangeServerIPs.ps1 @@ -55,7 +55,7 @@ function Get-ExchangeServerIPs { if (-not $IpsFound) { $FailedServers += $Server.Name - Write-Verbose "Ip of $($Server.Name) cannot be found and will not be added to ip allow list." + Write-Verbose "IP of $($Server.Name) cannot be found and will not be added to IP allow list." } $counter++ diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index ebb04e6ef8..fe6ba01dd2 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -30,7 +30,7 @@ function Get-IPRangeAllowListFromFile { } if ($null -eq $SubnetStrings -or $SubnetStrings.Length -eq 0) { - Write-Host "The Ip range file provided is empty. Please provide a valid file." -ForegroundColor Red + Write-Host "The IP range file provided is empty. Please provide a valid file." -ForegroundColor Red return } else { $ipRangesString = [string]::Join(", ", $SubnetStrings) From 925fdac348462b983c26dfb7bbf360a48ebec9c8 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 14:04:35 +0530 Subject: [PATCH 51/65] String changes --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 2 +- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 2 +- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index 861be70757..94a30489e5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -255,7 +255,7 @@ function Invoke-ConfigureMitigation { Write-Verbose ("No Local IPs detected for this server") } } else { - Write-Host ("Script failed to retrieve local IPs for server {0} with the Inner Exception:" -f $Server) -ForegroundColor Red + Write-Host ("Script failed to retrieve local IPs for server {0}. Reapply IP filtering on server. Inner Exception:" -f $Server) -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext $FailedServersFilter += $Server continue diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 9a7568d202..c2ec9626ab 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -166,7 +166,7 @@ function Invoke-RollbackIPFiltering { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Failed to rollback ip filtering rules on server $($Server.Name), because we weren't able to reach it." + $line = "Server Unreachable: Unable to rollback ip filtering rules on server $($Server.Name)." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServers[$_].Add($Server.Name) } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index e102d19be9..3e0bd0b84c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -194,7 +194,7 @@ function Invoke-ValidateMitigation { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Failed to validate ip filtering rules on server $($Server), because we weren't able to reach it." + $line = "Server Unreachable: Unable to validate ip filtering rules on server $($Server)." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServersEP[$_].Add($Server) } From 951dfac0add751c9db7b33a3611617e7fc210984 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 19:56:11 +0530 Subject: [PATCH 52/65] Resolved Nit comments; added functionality to display list of servers that are unchanged after applying mitigations --- .../Invoke-ConfigureMitigation.ps1 | 48 ++++++++++++------- .../Invoke-RollbackIPFiltering.ps1 | 12 ++--- .../Invoke-ValidateMitigation.ps1 | 25 ++++++---- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index d02cae20a5..e389b51a23 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -11,13 +11,14 @@ function Invoke-ConfigureMitigation { [Parameter(Mandatory = $true)] [string[]]$ExchangeServers, [Parameter(Mandatory = $true)] - [object[]]$ipRangeAllowListRules, + [object[]]$IPRangeAllowListRules , [Parameter(Mandatory = $true)] [string[]]$SiteVDirLocations ) begin { $FailedServersFilter = @{} + $UnchangedFilterServers = @{} $progressParams = @{ Activity = "Applying IP filtering Rules" @@ -43,7 +44,7 @@ function Invoke-ConfigureMitigation { ErrorContext = $null } - function Backup-currentIpFilteringRules { + function BackupCurrentIPFilteringRules { param( [Parameter(Mandatory = $true)] [string]$BackupPath, @@ -70,7 +71,7 @@ function Invoke-ConfigureMitigation { return $true } - function Get-LocalIpAddresses { + function GetLocalIPAddresses { $ips = New-Object 'System.Collections.Generic.List[string]' $interfaces = Get-NetIPAddress -ErrorAction Stop foreach ($interface in $interfaces) { @@ -97,12 +98,14 @@ function Invoke-ConfigureMitigation { $Filter = 'system.webServer/security/ipSecurity' $IISPath = 'IIS:\' $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) - $state.IsBackUpSuccessful = Backup-currentIpFilteringRules -BackupPath $backupPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules + $state.IsBackUpSuccessful = BackupCurrentIPFilteringRules -BackupPath $backupPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules $RulesToBeAdded = @() foreach ($IpFilteringRule in $IpFilteringRules) { - $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") } + $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and + ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") + } if ($null -eq $ExistingIPSubnetRule) { if ($IpFilteringRule.Type -eq "Single IP") { @@ -129,6 +132,7 @@ function Invoke-ConfigureMitigation { } if ($RulesToBeAdded.Count -gt 0) { + $state.AreIPRulesModified = $true Add-WebConfigurationProperty -Filter $Filter -PSPath $IISPath -Location $SiteVDirLocation -Name "." -Value $RulesToBeAdded -ErrorAction Stop -WhatIf:$WhatIf } @@ -152,7 +156,7 @@ function Invoke-ConfigureMitigation { $results.IsWindowsFeatureInstalled = $true - $localIPs = Get-LocalIpAddresses + $localIPs = GetLocalIPAddresses $results.IsGetLocalIPSuccessful = $true foreach ($localIP in $localIPs) { @@ -169,6 +173,7 @@ function Invoke-ConfigureMitigation { IsSetDefaultRuleSuccessful = $false ErrorContext = $null IPsNotAdded = New-Object 'System.Collections.Generic.List[string]' + AreIPRulesModified = $false } try { @@ -202,29 +207,30 @@ function Invoke-ConfigureMitigation { } process { $scriptblockArgs = [PSCustomObject]@{ SiteVDirLocations = $SiteVDirLocations - IpRangesForFiltering = $ipRangeAllowListRules + IpRangesForFiltering = $IPRangeAllowListRules PassedWhatIf = $WhatIfPreference } $counter = 0 $totalCount = $ExchangeServers.Count - if ($null -eq $ipRangeAllowListRules) { - $ipRangeAllowListString = "null" + if ($null -eq $IPRangeAllowListRules ) { + $IPRangeAllowListString = "null" } else { - $IpStrings = @() - $ipRangeAllowListRules | ForEach-Object { + $IPStrings = @() + $IPRangeAllowListRules | ForEach-Object { if ($_.Type -eq "Single IP") { - $IpStrings += $_.IP + $IPStrings += $_.IP } else { - $IpStrings += ("{0}/{1}" -f $_.IP, $_.SubnetMask) + $IPStrings += ("{0}/{1}" -f $_.IP, $_.SubnetMask) } } - $ipRangeAllowListString = [string]::Join(", ", $IpStrings) + $IPRangeAllowListString = [string]::Join(", ", $IPStrings) } $SiteVDirLocations | ForEach-Object { $FailedServersFilter[$_] = New-Object 'System.Collections.Generic.List[string]' + $UnchangedFilterServers[$_] = New-Object 'System.Collections.Generic.List[string]' } foreach ($Server in $ExchangeServers) { @@ -234,7 +240,7 @@ function Invoke-ConfigureMitigation { Write-Progress @progressParams $counter ++; - Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, ipRangeAllowListRules: {2}" -f $Server, $SiteVDirLocation, $ipRangeAllowListString) + Write-Verbose ("Calling Invoke-ScriptBlockHandler on Server {0} with arguments SiteVDirLocation: {1}, IPRangeAllowListRules : {2}" -f $Server, $SiteVDirLocation, $IPRangeAllowListString) $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ConfigureMitigation -ArgumentList $scriptblockArgs Write-Verbose ("Adding IP Restriction rules on Server {0}" -f $Server) @@ -274,7 +280,13 @@ function Invoke-ConfigureMitigation { } if ($state.IsCreateIPRulesSuccessful) { - Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation on server $Server") + if (-not $state.AreIPRulesModified) { + Write-Verbose ("No changes were made to IP filtering rules for VDir $SiteVDirLocation on server $Server") + $UnchangedFilterServers[$SiteVDirLocations] += $Server + } else { + Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation on server $Server") + } + if ($state.IPsNotAdded.Length -gt 0) { $line = ("Some IPs provided in the IPRange file were present in deny rules, hence these IPs were not added in the Allow List for VDir $SiteVDirLocation on server $Server. If you wish to add these IPs in allow list, remove these IPs from deny list in module name and reapply IP restrictions again.") Write-Warning ($line + "Check logs for further details.") @@ -305,6 +317,10 @@ function Invoke-ConfigureMitigation { if ($FailedServersFilter[$SiteVDirLocation].Length -gt 0) { Write-Host ("Unable to create IP Filtering Rules for VDir $SiteVDirLocation on the following servers: {0}" -f [string]::Join(", ", $FailedServersFilter[$SiteVDirLocation])) -ForegroundColor Red } + + if ($UnchangedFilterServers[$SiteVDirLocation].Length -gt 0) { + Write-Host ("No changes in IP Restriction rules for VDir $SiteVDirLocation in : {0}" -f [string]::Join(", ", $UnchangedFilterServers[$SiteVDirLocation])) + } } } } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index e5379883f7..56ccf44a66 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -37,7 +37,7 @@ function Invoke-RollbackIPFiltering { $results = @{} - function Backup-currentIpFilteringRules { + function BackupCurrentIPFilteringRules { param( [Parameter(Mandatory = $true)] [string]$BackupPath, @@ -64,7 +64,7 @@ function Invoke-RollbackIPFiltering { return $true } - function Restore-OriginalIpFilteringRules { + function RestoreOriginalIPFilteringRules { param( [Parameter(Mandatory = $true)] [string]$Filter, @@ -91,7 +91,7 @@ function Invoke-RollbackIPFiltering { return $true } - function TurnONEP { + function TurnONExtendedProtection { param( [Parameter(Mandatory = $true)] [string]$Filter, @@ -123,15 +123,15 @@ function Invoke-RollbackIPFiltering { } $state.RestoreFileExists = $true - TurnONEP -Filter $FilterEP -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation + TurnONExtendedProtection -Filter $FilterEP -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation $state.TurnOnEPSuccessful = $true $state.BackUpPath = "$($env:WINDIR)\System32\inetsrv\config\IpFilteringRules_" + $SiteVDirLocation.Replace('/', '-') + "_$([DateTime]::Now.ToString("yyyyMMddHHMMss")).bak" $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) - $state.BackupCurrentSuccessful = Backup-currentIpFilteringRules -BackupPath $state.BackUpPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules + $state.BackupCurrentSuccessful = BackupCurrentIPFilteringRules -BackupPath $state.BackUpPath -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation -ExistingRules $ExistingRules $originalIpFilteringConfigurations = (Get-Content $state.RestorePath | Out-String | ConvertFrom-Json) - $state.RestoreSuccessful = Restore-OriginalIpFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation + $state.RestoreSuccessful = RestoreOriginalIPFilteringRules -OriginalIpFilteringRules ($originalIpFilteringConfigurations.Rules) -DefaultForUnspecifiedIPs ($originalIpFilteringConfigurations.DefaultForUnspecifiedIPs) -Filter $Filter -IISPath $IISPath -SiteVDirLocation $SiteVDirLocation } catch { $state.ErrorContext = $_ } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 85beb0a078..7975a2716d 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -54,7 +54,7 @@ function Invoke-ValidateMitigation { $results = @{} - function Get-LocalIpAddresses { + function GetLocalIPAddresses { $ips = New-Object 'System.Collections.Generic.List[string]' $interfaces = Get-NetIPAddress foreach ($interface in $interfaces) { @@ -67,7 +67,7 @@ function Invoke-ValidateMitigation { } # Set EP to None - function GetEPState { + function GetExtendedProtectionState { param ( [Parameter(Mandatory = $true)] [string]$SiteVDirLocation @@ -103,7 +103,12 @@ function Invoke-ValidateMitigation { $ExistingRules = @(Get-WebConfigurationProperty -Filter $Filter -Location $SiteVDirLocation -name collection) foreach ($IpFilteringRule in $IpFilteringRules) { - $ExistingIPSubnetRule = $ExistingRules | Where-Object { $_.ipAddress -eq $IpFilteringRule.IP -and ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") -and $_.allowed -eq $IpFilteringRule.Allowed } + $ExistingIPSubnetRule = $ExistingRules | Where-Object { + $_.ipAddress -eq $IpFilteringRule.IP -and + ($_.subnetMask -eq $IpFilteringRule.SubnetMask -or $IpFilteringRule.Type -eq "Single IP") -and + $_.allowed -eq $IpFilteringRule.Allowed + } + if ($null -eq $ExistingIPSubnetRule) { if ($IpFilteringRule.Type -eq "Single IP") { $IpString = $IpFilteringRule.IP @@ -134,7 +139,7 @@ function Invoke-ValidateMitigation { ErrorContext = $null } - $EPState = GetEPState -SiteVDirLocation $SiteVDirLocation + $EPState = GetExtendedProtectionState -SiteVDirLocation $SiteVDirLocation if ($EPState -eq "None") { $state.IsEPOff = $true } else { @@ -144,7 +149,7 @@ function Invoke-ValidateMitigation { $state.IsEPVerified = $true if ($null -ne $IpRangesForFiltering) { - $localIPs = Get-LocalIpAddresses + $localIPs = GetLocalIPAddresses $localIPs | ForEach-Object { $IpRangesForFiltering += @{Type="Single IP"; IP=$_; Allowed=$true } @@ -208,7 +213,7 @@ function Invoke-ValidateMitigation { if ($state.IsEPOff) { Write-Verbose ("Expected: The state of Extended protection flag is None for Vdir $($SiteVDirLocation) on server $Server") } elseif ($state.IsEPVerified) { - Write-Host ("Unexpected: The state of Extended protection flag is not set to None for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: The state of Extended protection flag is not set to None for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red $UnMitigatedServersEP[$SiteVDirLocation] += $Server } else { Write-Host ("Unknown: Script failed to get state of Extended protection flag for Vdir $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red @@ -226,7 +231,7 @@ function Invoke-ValidateMitigation { $FailedServersFilter[$SiteVDirLocation] += $Server continue } elseif (-not $state.IsWindowsFeatureInstalled) { - Write-Host ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red $IsFilterUnMitigated = $true } else { Write-Verbose ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server for Vdir $($SiteVDirLocation) on server $Server") @@ -236,7 +241,7 @@ function Invoke-ValidateMitigation { $FailedServersFilter[$SiteVDirLocation] += $Server continue } elseif ($null -ne $state.RulesNotFound -and $state.RulesNotFound.Length -gt 0) { - Write-Host ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $state.RulesNotFound)) $IsFilterUnMitigated = $true } else { @@ -246,7 +251,7 @@ function Invoke-ValidateMitigation { if ($state.IsDefaultFilterDeny) { Write-Verbose ("Expected: The default IP Filtering rule is set to deny for Vdir $($SiteVDirLocation) on server $Server") } elseif ($state.IsDefaultFilterVerified) { - Write-Host ("Unexpected: The default IP Filtering rule is not set to deny for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: The default IP Filtering rule is not set to deny for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red $IsFilterUnMitigated = $true } else { Write-Host ("Unknown: Script failed to get the default IP Filtering rule for Vdir $($SiteVDirLocation) on server $Server with Inner Exception") -ForegroundColor Red @@ -270,7 +275,7 @@ function Invoke-ValidateMitigation { } if ($UnMitigatedServersFilter[$SiteVDirLocation].Length -gt 0) { - Write-Host ("IP Filtering Rules or Default IP rule on the following servers is not as expected for VDir {0}: {1}" -f $SiteVDirLocation, [string]::Join(", ", $UnMitigatedServersFilter[$SiteVDirLocation])) -ForegroundColor Red + Write-Host ("IP Filtering Rules or Default IP rule on the following servers does not contain all the IP Ranges/addresses provided for validation in VDir {0}: {1}" -f $SiteVDirLocation, [string]::Join(", ", $UnMitigatedServersFilter[$SiteVDirLocation])) -ForegroundColor Red $FoundFailedOrUnmitigated = $true } From c225370fdade09649fe3572d88e4f147c5d8fc6d Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Wed, 17 Aug 2022 20:15:01 +0530 Subject: [PATCH 53/65] Replaced GetCommaSaperatedString function with string::join --- .../Invoke-ConfigureMitigation.ps1 | 18 ++---------------- .../Invoke-ValidateMitigation.ps1 | 15 +-------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index e389b51a23..a87a243471 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -190,20 +190,6 @@ function Invoke-ConfigureMitigation { return $results } - - function GetCommaSaperatedString { - param( - [Parameter(Mandatory = $true)] - [object[]]$list - ) - - $string = "" - foreach ($element in $list) { - $string += ($element.ToString() + ", ") - } - - return $string.Trim(", ") - } } process { $scriptblockArgs = [PSCustomObject]@{ SiteVDirLocations = $SiteVDirLocations @@ -256,7 +242,7 @@ function Invoke-ConfigureMitigation { if ($resultsInvoke.IsGetLocalIPSuccessful) { Write-Verbose ("Successfully retrieved local IPs for the server") if ($null -ne $resultsInvoke.LocalIPs -and $resultsInvoke.LocalIPs.Length -gt 0) { - Write-Verbose ("Local IPs detected for this server: {0}" -f (GetCommaSaperatedString -list $resultsInvoke.LocalIPs)) + Write-Verbose ("Local IPs detected for this server: {0}" -f [string]::Join(", ", [string[]]$resultsInvoke.LocalIPs)) } else { Write-Verbose ("No Local IPs detected for this server") } @@ -291,7 +277,7 @@ function Invoke-ConfigureMitigation { $line = ("Some IPs provided in the IPRange file were present in deny rules, hence these IPs were not added in the Allow List for VDir $SiteVDirLocation on server $Server. If you wish to add these IPs in allow list, remove these IPs from deny list in module name and reapply IP restrictions again.") Write-Warning ($line + "Check logs for further details.") Write-Verbose $line - Write-Verbose (GetCommaSaperatedString -list $state.IPsNotAdded) + Write-Verbose ([string]::Join(", ", $state.IPsNotAdded)) } } else { Write-Host ("Script failed to update IP filtering allow list for VDir $SiteVDirLocation on server $Server with the Inner Exception:") -ForegroundColor Red diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 7975a2716d..af15745f7c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -31,19 +31,6 @@ function Invoke-ValidateMitigation { Write-Verbose "Calling: $($MyInvocation.MyCommand)" - function GetCommaSaperatedString { - param( - $list - ) - - $string = "" - foreach ($element in $list) { - $string += ($element.ToString() + ", ") - } - - return $string.Trim(", ") - } - $ValidateMitigationScriptBlock = { param( [Object]$Arguments @@ -242,7 +229,7 @@ function Invoke-ValidateMitigation { continue } elseif ($null -ne $state.RulesNotFound -and $state.RulesNotFound.Length -gt 0) { Write-Verbose ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red - Write-Verbose ("Following Rules weren't found: {0}" -f (GetCommaSaperatedString -list $state.RulesNotFound)) + Write-Verbose ("Following Rules weren't found: {0}" -f [string]::Join(", ", [string[]]$state.RulesNotFound)) $IsFilterUnMitigated = $true } else { Write-Verbose ("Expected: Successfully verified all the IP filtering rules for Vdir $($SiteVDirLocation) on server $Server") From f70f957544007873d556ce3bc3911e9813b8ed5e Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 22:10:52 +0530 Subject: [PATCH 54/65] Fix failed server configure mitigation issue --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index c9d62f62b1..ac05bbefd5 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -235,7 +235,7 @@ function Invoke-ConfigureMitigation { } else { Write-Host ("Script failed to install windows feature - Web-IP-Security on server {0} with the Inner Exception:" -f $Server) -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext - $FailedServersFilter += $Server + $FailedServersFilter[$SiteVDirLocation] += $Server continue } @@ -249,7 +249,7 @@ function Invoke-ConfigureMitigation { } else { Write-Host ("Script failed to retrieve local IPs for server {0}. Reapply IP filtering on server. Inner Exception:" -f $Server) -ForegroundColor Red Write-HostErrorInformation $resultsInvoke.ErrorContext - $FailedServersFilter += $Server + $FailedServersFilter[$SiteVDirLocation] += $Server continue } From 3f4482fdda07987379dee2e32004247d9298c198 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 22:21:50 +0530 Subject: [PATCH 55/65] Configure Mitigations already applied message fix --- .../ConfigurationAction/Invoke-ConfigureMitigation.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index ac05bbefd5..a4db36b5ec 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -268,7 +268,7 @@ function Invoke-ConfigureMitigation { if ($state.IsCreateIPRulesSuccessful) { if (-not $state.AreIPRulesModified) { Write-Verbose ("No changes were made to IP filtering rules for VDir $SiteVDirLocation on server $Server") - $UnchangedFilterServers[$SiteVDirLocations] += $Server + $UnchangedFilterServers[$SiteVDirLocation] += $Server } else { Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation on server $Server") } From 3051cac225cbc080550b078fc790f157610a0aa1 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 22:31:40 +0530 Subject: [PATCH 56/65] Change writing successful application of rules (when nothing changes) --- .../Invoke-ConfigureMitigation.ps1 | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 index a4db36b5ec..5754d7ed4d 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ConfigureMitigation.ps1 @@ -266,19 +266,19 @@ function Invoke-ConfigureMitigation { } if ($state.IsCreateIPRulesSuccessful) { - if (-not $state.AreIPRulesModified) { - Write-Verbose ("No changes were made to IP filtering rules for VDir $SiteVDirLocation on server $Server") - $UnchangedFilterServers[$SiteVDirLocation] += $Server - } else { - Write-Verbose ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation on server $Server") - } - if ($state.IPsNotAdded.Length -gt 0) { $line = ("Some IPs provided in the IPRange file were present in deny rules, hence these IPs were not added in the Allow List for VDir $SiteVDirLocation on server $Server. If you wish to add these IPs in allow list, remove these IPs from deny list in module name and reapply IP restrictions again.") Write-Warning ($line + "Check logs for further details.") Write-Verbose $line Write-Verbose ([string]::Join(", ", $state.IPsNotAdded)) } + + if (-not $state.AreIPRulesModified) { + Write-Verbose ("No changes were made to IP filtering rules for VDir $SiteVDirLocation on server $Server") + $UnchangedFilterServers[$SiteVDirLocation] += $Server + } else { + Write-Host ("Successfully updated IP filtering allow list for VDir $SiteVDirLocation on server $Server") + } } else { Write-Host ("Script failed to update IP filtering allow list for VDir $SiteVDirLocation on server $Server with the Inner Exception:") -ForegroundColor Red Write-HostErrorInformation $state.ErrorContext @@ -294,8 +294,6 @@ function Invoke-ConfigureMitigation { $FailedServersFilter[$SiteVDirLocation] += $Server continue } - - Write-Host ("Enabled IP filtering rules on server {0} for VDir $SiteVDirLocation" -f $Server) } } } end { @@ -305,7 +303,7 @@ function Invoke-ConfigureMitigation { } if ($UnchangedFilterServers[$SiteVDirLocation].Length -gt 0) { - Write-Host ("No changes in IP Restriction rules for VDir $SiteVDirLocation in : {0}" -f [string]::Join(", ", $UnchangedFilterServers[$SiteVDirLocation])) + Write-Host ("IP Restrictions are applied. No changes made in IP Restriction rules for VDir $SiteVDirLocation in : {0}" -f [string]::Join(", ", $UnchangedFilterServers[$SiteVDirLocation])) } } } From 94ac64d57c993b28a2b16a9702dfa71caff81aab Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Wed, 17 Aug 2022 22:49:41 +0530 Subject: [PATCH 57/65] ValidateMitigation write-verbose foreground color fix --- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index 45a613aa15..ec88f0e80e 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -200,7 +200,7 @@ function Invoke-ValidateMitigation { if ($state.IsEPOff) { Write-Verbose ("Expected: The state of Extended protection flag is None for Vdir $($SiteVDirLocation) on server $Server") } elseif ($state.IsEPVerified) { - Write-Verbose ("Unexpected: The state of Extended protection flag is not set to None for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: The state of Extended protection flag is not set to None for Vdir $($SiteVDirLocation) on server $Server") $UnMitigatedServersEP[$SiteVDirLocation] += $Server } else { Write-Host ("Unknown: Script failed to get state of Extended protection flag for Vdir $($SiteVDirLocation) with Inner Exception") -ForegroundColor Red @@ -218,7 +218,7 @@ function Invoke-ValidateMitigation { $FailedServersFilter[$SiteVDirLocation] += $Server continue } elseif (-not $state.IsWindowsFeatureInstalled) { - Write-Verbose ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: Windows feature Web-IP-Security is not present on the server for Vdir $($SiteVDirLocation) on server $Server") $IsFilterUnMitigated = $true } else { Write-Verbose ("Expected: Successfully verified that the Windows feature Web-IP-Security is present on the server for Vdir $($SiteVDirLocation) on server $Server") @@ -228,7 +228,7 @@ function Invoke-ValidateMitigation { $FailedServersFilter[$SiteVDirLocation] += $Server continue } elseif ($null -ne $state.RulesNotFound -and $state.RulesNotFound.Length -gt 0) { - Write-Verbose ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: Some or all the rules present in the file specified aren't applied for Vdir $($SiteVDirLocation) on server $Server") Write-Verbose ("Following Rules weren't found: {0}" -f [string]::Join(", ", [string[]]$state.RulesNotFound)) $IsFilterUnMitigated = $true } else { @@ -238,7 +238,7 @@ function Invoke-ValidateMitigation { if ($state.IsDefaultFilterDeny) { Write-Verbose ("Expected: The default IP Filtering rule is set to deny for Vdir $($SiteVDirLocation) on server $Server") } elseif ($state.IsDefaultFilterVerified) { - Write-Verbose ("Unexpected: The default IP Filtering rule is not set to deny for Vdir $($SiteVDirLocation) on server $Server") -ForegroundColor Red + Write-Verbose ("Unexpected: The default IP Filtering rule is not set to deny for Vdir $($SiteVDirLocation) on server $Server") $IsFilterUnMitigated = $true } else { Write-Host ("Unknown: Script failed to get the default IP Filtering rule for Vdir $($SiteVDirLocation) on server $Server with Inner Exception") -ForegroundColor Red From 7aae2632291dad292dcec99115a68a30ccdb795b Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Thu, 18 Aug 2022 12:46:12 +0530 Subject: [PATCH 58/65] string changes --- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 4 ++-- .../ExchangeExtendedProtectionManagement.ps1 | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 08e6752c5f..80e88a3ee1 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -178,7 +178,7 @@ function Invoke-RollbackIPFiltering { $state = $resultsInvoke[$SiteVDirLocation] if ($state.RestoreFileExists) { if ($state.TurnOnEPSuccessful) { - Write-Host "Turned on EP on server $($Server.Name) for VDir $SiteVDirLocation" + Write-Host "Turned on Extended Protection on server $($Server.Name) for VDir $SiteVDirLocation" if ($state.BackupCurrentSuccessful) { Write-Verbose "Successfully backed up current configuration on server $($Server.Name) at $($state.BackUpPath) for VDir $SiteVDirLocation" if ($state.RestoreSuccessful) { @@ -194,7 +194,7 @@ function Invoke-RollbackIPFiltering { $Failed = $true } } else { - Write-Host "Failed to turn on EP on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red + Write-Host "Failed to turn on Extended Protection on server $($Server.Name). Aborting rollback on the server $($Server.Name) for VDir $SiteVDirLocation. Inner Exception:" -ForegroundColor Red Write-HostErrorInformation $state.ErrorContext $Failed = $true } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index e7b9d1fc01..66273ebcd3 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -237,6 +237,10 @@ begin { if ($FindExchangeServerIPAddresses) { Get-ExchangeServerIPs -OutputFilePath $OutputFilePath -ExchangeServers $ExchangeServers + Write-Warning "The file generated contains all the IPv4 and IPv6 addresses of all Exchange Servers in the organization." + + " This file should be used as a reference. Please change the file to include/remove IP addresses for the IP filtering allow list." + + " If the number of Exchange Servers in your organanization is high (>100), consider using a IPRange file with IP Range Subnets [x.x.x.x/n] instead of IP addresses which is more efficient." + + "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc." return } From 8aa201652d5fe4f15363886fa626c4155c3b7303 Mon Sep 17 00:00:00 2001 From: Akash Sharma Date: Thu, 18 Aug 2022 13:20:18 +0530 Subject: [PATCH 59/65] Fix for findip warning --- .../ExchangeExtendedProtectionManagement.ps1 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 66273ebcd3..80f780ec37 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -237,10 +237,10 @@ begin { if ($FindExchangeServerIPAddresses) { Get-ExchangeServerIPs -OutputFilePath $OutputFilePath -ExchangeServers $ExchangeServers - Write-Warning "The file generated contains all the IPv4 and IPv6 addresses of all Exchange Servers in the organization." + - " This file should be used as a reference. Please change the file to include/remove IP addresses for the IP filtering allow list." + - " If the number of Exchange Servers in your organanization is high (>100), consider using a IPRange file with IP Range Subnets [x.x.x.x/n] instead of IP addresses which is more efficient." + - "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc." + Write-Warning ("The file generated contains all the IPv4 and IPv6 addresses of all Exchange Servers in the organization." + + " This file should be used as a reference. Please change the file to include/remove IP addresses for the IP filtering allow list." + + " If the number of Exchange Servers in your organanization is high (>100), consider using a IPRange file with IP Range Subnets [x.x.x.x/n] instead of IP addresses which is more efficient." + + "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc.") return } From a2877212836930c533df5abf1e51c6f2281ea930 Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Mon, 22 Aug 2022 16:26:01 +0530 Subject: [PATCH 60/65] Changing ValidateMitigation flag to ValidateType --- .../ExchangeExtendedProtectionManagement.ps1 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 66273ebcd3..260dde5bd6 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -73,7 +73,7 @@ param( [ValidateScript({ ($null -ne $_) -and ($_.Length -gt 0) })] - [string[]]$ValidateMitigation, + [string[]]$ValidateType, [Parameter (Mandatory = $true, ParameterSetName = 'Rollback', HelpMessage = "Using this parameter will allow you to rollback using the type you specified.")] [ValidateSet('RestrictTypeEWSBackend', 'RestoreIISAppConfig')] @@ -140,7 +140,7 @@ begin { $ConfigureMitigationSelected = $PsCmdlet.ParameterSetName -eq "ConfigureMitigation" $ConfigureEPSelected = $ConfigureMitigationSelected -or ($PsCmdlet.ParameterSetName -eq "ConfigureEP" -and -not $ShowExtendedProtection) - $ValidateMitigationSelected = $PsCmdlet.ParameterSetName -eq "ValidateMitigation" + $ValidateTypeSelected = $PsCmdlet.ParameterSetName -eq "ValidateMitigation" $includeExchangeServerNames = New-Object 'System.Collections.Generic.List[string]' @@ -157,12 +157,12 @@ begin { $RestrictType = $RestrictType | Get-Unique } - if ($ValidateMitigationSelected) { + if ($ValidateTypeSelected) { $RestrictType = New-Object 'System.Collections.Generic.List[string]' - $ValidateMitigation | Get-Unique | ForEach-Object { $RestrictType += $_.Replace("RestrictType", "") } + $ValidateType | Get-Unique | ForEach-Object { $RestrictType += $_.Replace("RestrictType", "") } } - if (($ConfigureMitigationSelected -or $ValidateMitigationSelected)) { + if (($ConfigureMitigationSelected -or $ValidateTypeSelected)) { # Get list of IPs in object form from the file specified $ipResults = Get-IPRangeAllowListFromFile -FilePath $IPRangeFilePath if ($ipResults.IsError) { @@ -263,7 +263,7 @@ begin { exit } - if ($ValidateMitigationSelected) { + if ($ValidateTypeSelected) { # Validate mitigation Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } From 81cad04ff289f7166cd446409d2c301f75512677 Mon Sep 17 00:00:00 2001 From: akshar Date: Thu, 8 Sep 2022 20:33:47 +0530 Subject: [PATCH 61/65] Don't consider E15 servers for mitigation --- .../ExchangeExtendedProtectionManagement.ps1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 0f329fa3f9..55981ee024 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -265,6 +265,7 @@ begin { if ($ValidateTypeSelected) { # Validate mitigation + $ExchangeServers = $ExchangeServers | Where-Object { !($_.AdminDisplayVersion.Major -eq 15 -and $_.AdminDisplayVersion.Minor -eq 0 -and $_.IsClientAccessServer) } Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } @@ -558,6 +559,7 @@ begin { if ($ConfigureMitigationSelected) { # Apply rules + $ExchangeServers = $ExchangeServers | Where-Object { !($_.AdminDisplayVersion.Major -eq 15 -and $_.AdminDisplayVersion.Minor -eq 0 -and $_.IsClientAccessServer) } Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } } elseif ($RollbackSelected) { @@ -568,6 +570,7 @@ begin { } if ($RollbackRestrictType) { + $ExchangeServers = $ExchangeServers | Where-Object { !($_.AdminDisplayVersion.Major -eq 15 -and $_.AdminDisplayVersion.Minor -eq 0 -and $_.IsClientAccessServer) } Invoke-RollbackIPFiltering -ExchangeServers $ExchangeServers -SiteVDirLocations $SiteVDirLocations } @@ -576,4 +579,4 @@ begin { } finally { Write-Host "Do you have feedback regarding the script? Please email ExToolsFeedback@microsoft.com." } -} +} From 987aa9ac47d71cd8332aef3f211dcf768bdc0cde Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Fri, 9 Sep 2022 17:58:53 +0530 Subject: [PATCH 62/65] resolved comments on build version --- .../ExchangeExtendedProtectionManagement.ps1 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 55981ee024..192be541c3 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -98,6 +98,7 @@ begin { . $PSScriptRoot\..\..\..\Shared\LoggerFunctions.ps1 . $PSScriptRoot\..\..\..\Shared\Show-Disclaimer.ps1 . $PSScriptRoot\..\..\..\Shared\Write-Host.ps1 + . $PSScriptRoot\..\..\..\Shared\Get-ExchangeBuildVersionInformation.ps1 # TODO: Move this so it isn't duplicated # matching restrictions @@ -265,7 +266,7 @@ begin { if ($ValidateTypeSelected) { # Validate mitigation - $ExchangeServers = $ExchangeServers | Where-Object { !($_.AdminDisplayVersion.Major -eq 15 -and $_.AdminDisplayVersion.Minor -eq 0 -and $_.IsClientAccessServer) } + $ExchangeServers = $ExchangeServers | Where-Object { -not ((Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion).Major -eq 15 -and (Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion).Minor -eq 0 -and $_.IsClientAccessServer) } Invoke-ValidateMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } @@ -559,7 +560,7 @@ begin { if ($ConfigureMitigationSelected) { # Apply rules - $ExchangeServers = $ExchangeServers | Where-Object { !($_.AdminDisplayVersion.Major -eq 15 -and $_.AdminDisplayVersion.Minor -eq 0 -and $_.IsClientAccessServer) } + $ExchangeServers = $ExchangeServers | Where-Object { -not ((Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion).Major -eq 15 -and (Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion).Minor -eq 0 -and $_.IsClientAccessServer) } Invoke-ConfigureMitigation -ExchangeServers $ExchangeServers.Name -ipRangeAllowListRules $ipRangeAllowListRules -SiteVDirLocations $SiteVDirLocations } } elseif ($RollbackSelected) { @@ -570,7 +571,7 @@ begin { } if ($RollbackRestrictType) { - $ExchangeServers = $ExchangeServers | Where-Object { !($_.AdminDisplayVersion.Major -eq 15 -and $_.AdminDisplayVersion.Minor -eq 0 -and $_.IsClientAccessServer) } + $ExchangeServers = $ExchangeServers | Where-Object { -not ((Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion).Major -eq 15 -and (Get-ExchangeBuildVersionInformation -AdminDisplayVersion $_.AdminDisplayVersion).Minor -eq 0 -and $_.IsClientAccessServer) } Invoke-RollbackIPFiltering -ExchangeServers $ExchangeServers -SiteVDirLocations $SiteVDirLocations } From f20b3cc95e506b4f293581cdad2b88b7ef5affdb Mon Sep 17 00:00:00 2001 From: guptavarun-msft Date: Mon, 12 Sep 2022 18:40:28 +0530 Subject: [PATCH 63/65] Nit changes (ip -> IP) --- .../ConfigurationAction/Invoke-RollbackIPFiltering.ps1 | 2 +- .../ConfigurationAction/Invoke-ValidateMitigation.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 index 80e88a3ee1..85f1c0056c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-RollbackIPFiltering.ps1 @@ -166,7 +166,7 @@ function Invoke-RollbackIPFiltering { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server.Name -ScriptBlock $RollbackIPFiltering -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Server Unreachable: Unable to rollback ip filtering rules on server $($Server.Name)." + $line = "Server Unreachable: Unable to rollback IP filtering rules on server $($Server.Name)." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServers[$_].Add($Server.Name) } diff --git a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 index ec88f0e80e..5d706e7ad3 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ConfigurationAction/Invoke-ValidateMitigation.ps1 @@ -186,7 +186,7 @@ function Invoke-ValidateMitigation { $resultsInvoke = Invoke-ScriptBlockHandler -ComputerName $Server -ScriptBlock $ValidateMitigationScriptBlock -ArgumentList $scriptblockArgs if ($null -eq $resultsInvoke) { - $line = "Server Unreachable: Unable to validate ip filtering rules on server $($Server)." + $line = "Server Unreachable: Unable to validate IP filtering rules on server $($Server)." Write-Verbose $line Write-Warning $line $SiteVDirLocations | ForEach-Object { $FailedServersEP[$_].Add($Server) } From fddbd47a512120a72dbcdf22951e57b13da63501 Mon Sep 17 00:00:00 2001 From: akshar Date: Mon, 12 Sep 2022 19:58:53 +0530 Subject: [PATCH 64/65] Removed extra code that was not needed --- .../DataCollection/Get-IPRangeAllowListFromFile.ps1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 index fe6ba01dd2..5053fffad2 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/DataCollection/Get-IPRangeAllowListFromFile.ps1 @@ -65,11 +65,9 @@ function Get-IPRangeAllowListFromFile { $InvalidSubnetMaskString = "$baseError Invalid Subnet Mask found: The Subnet Mask $SubnetMaskString is not in valid range.Note: Subnet Mask must be either empty or a non-negative integer. For IPv4 the value must be <= 32 and for IPv6 the value must be <= 128. Re-execute the command with proper input file for IPRange parameter." if ($null -eq $SubnetMask) { Write-Host ($InvalidSubnetMaskString) -ForegroundColor Red - $results.IsError = $true return } elseif (($SubnetMask -gt 32 -and -not $IsIPv6) -or $SubnetMask -gt 128 -or $SubnetMask -lt 0) { Write-Host ($InvalidSubnetMaskString) -ForegroundColor Red - $results.IsError = $true return } From cf98f2fab07feddd5339e02a4e85aa3d2b6f2e01 Mon Sep 17 00:00:00 2001 From: David Paulson Date: Thu, 15 Sep 2022 09:03:10 -0500 Subject: [PATCH 65/65] Fixed typo --- .../ExchangeExtendedProtectionManagement.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 index 57432a6284..a4d337700c 100644 --- a/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 +++ b/Security/src/ExchangeExtendedProtectionManagement/ExchangeExtendedProtectionManagement.ps1 @@ -249,7 +249,7 @@ begin { Get-ExchangeServerIPs -OutputFilePath $OutputFilePath -ExchangeServers $ExchangeServers Write-Warning ("The file generated contains all the IPv4 and IPv6 addresses of all Exchange Servers in the organization." + " This file should be used as a reference. Please change the file to include/remove IP addresses for the IP filtering allow list." + - " If the number of Exchange Servers in your organanization is high (>100), consider using a IPRange file with IP Range Subnets [x.x.x.x/n] instead of IP addresses which is more efficient." + + " If the number of Exchange Servers in your organization is high (>100), consider using a IPRange file with IP Range Subnets [x.x.x.x/n] instead of IP addresses which is more efficient." + "`r`nYou can find more information on: https://aka.ms/ExchangeEPDoc.") return }