
Secure Configuration Baselines
+SCuBA M365 Security Baseline Conformance Reports
-{TENANT_NAME} tenant
+ {TENANT_DETAILS} +{TABLES}
From 07b95a04fb709eb30db19874f96603f6e2b2cf6d Mon Sep 17 00:00:00 2001 From: Ethan <98476160+ethanb-cisa@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:13:59 -0500 Subject: [PATCH] v0.2.0 release (#24) * v0.2.0 release --- .gitattributes | 1 + .github/workflows/run_opa_tests.yaml | 3 +- .gitignore | 13 +- AllowBasicAuthentication.ps1 | 62 +- PowerShell/ScubaGear/Dependencies.ps1 | 41 + .../Modules/Connection/ConnectHelpers.psm1 | 33 + .../Modules/Connection/Connection.psm1 | 278 +- .../Modules/CreateReport/CreateReport.psm1 | 69 +- .../CreateReport/ParentReportTemplate.html | 12 +- .../Modules/CreateReport/ParentStyle.css | 11 +- .../Modules/CreateReport/ReportTemplate.html | 9 +- .../ScubaGear/Modules/CreateReport/main.css | 29 +- .../ScubaGear/Modules/CreateReport/main.js | 5 + .../ScubaGear/Modules/Orchestrator.psm1 | 699 +- .../Modules/Providers/ExportAADProvider.psm1 | 31 +- .../Providers/ExportDefenderProvider.psm1 | 151 +- .../Modules/Providers/ExportEXOProvider.psm1 | 342 +- .../Providers/ExportOneDriveProvider.psm1 | 29 +- .../ExportPowerPlatformProvider.psm1 | 52 +- .../Providers/ExportSharePointProvider.psm1 | 28 +- .../Providers/ExportTeamsProvider.psm1 | 74 +- .../ProviderHelpers/CommandTracker.psm1 | 65 + .../ProviderHelpers/SPOSiteHelper.psm1 | 39 + PowerShell/ScubaGear/RequiredVersions.ps1 | 93 + PowerShell/ScubaGear/ScubaGear.psd1 | Bin 8554 -> 4120 bytes PowerShell/ScubaGear/ScubaGear.psm1 | 2 + README.md | 310 +- Rego/AADConfig.rego | 6 +- Rego/DefenderConfig.rego | 246 +- Rego/EXOConfig.rego | 145 +- Rego/OneDriveConfig.rego | 85 +- Rego/SharepointConfig.rego | 89 +- Rego/TeamsConfig.rego | 122 +- SetUp.ps1 | 137 +- Testing/Functional/Auto/ExtremeTest.txt | 1956 +++ Testing/Functional/Auto/MinimumTest.txt | 41 + Testing/Functional/Auto/SimpleTest.txt | 6 + .../Functional/RegoCachedProviderTesting.ps1 | 64 + Testing/RunFunctionalTests.ps1 | 471 + Testing/RunUnitTests.ps1 | 218 + Testing/Unit/Rego/AAD/AADConfig2_01_test.rego | 198 + Testing/Unit/Rego/AAD/AADConfig2_02_test.rego | 274 + Testing/Unit/Rego/AAD/AADConfig2_03_test.rego | 222 + Testing/Unit/Rego/AAD/AADConfig2_04_test.rego | 209 + Testing/Unit/Rego/AAD/AADConfig2_05_test.rego | 35 + Testing/Unit/Rego/AAD/AADConfig2_06_test.rego | 47 + Testing/Unit/Rego/AAD/AADConfig2_07_test.rego | 130 + Testing/Unit/Rego/AAD/AADConfig2_08_test.rego | 19 + Testing/Unit/Rego/AAD/AADConfig2_09_test.rego | 250 + Testing/Unit/Rego/AAD/AADConfig2_10_test.rego | 210 + Testing/Unit/Rego/AAD/AADConfig2_11_test.rego | 86 + Testing/Unit/Rego/AAD/AADConfig2_12_test.rego | 78 + Testing/Unit/Rego/AAD/AADConfig2_13_test.rego | 248 + Testing/Unit/Rego/AAD/AADConfig2_14_test.rego | 199 + Testing/Unit/Rego/AAD/AADConfig2_15_test.rego | 80 + Testing/Unit/Rego/AAD/AADConfig2_16_test.rego | 359 + Testing/Unit/Rego/AAD/AADConfig2_17_test.rego | 192 + Testing/Unit/Rego/AAD/AADConfig2_18_test.rego | 133 + .../Defender/DefenderConfig2_01_test.rego | 119 + .../Defender/DefenderConfig2_02_test.rego | 888 ++ .../Defender/DefenderConfig2_03_test.rego | 198 + .../Defender/DefenderConfig2_04_test.rego | 70 + .../Defender/DefenderConfig2_05_test.rego | 1377 ++ .../Defender/DefenderConfig2_06_test.rego | 1278 ++ .../Defender/DefenderConfig2_07_test.rego | 1412 ++ .../Defender/DefenderConfig2_08_test.rego | 466 + .../Defender/DefenderConfig2_09_test.rego | 233 + .../Defender/DefenderConfig2_10_test.rego | 74 + Testing/Unit/Rego/EXO/EXOConfig2_01_test.rego | 102 + Testing/Unit/Rego/EXO/EXOConfig2_02_test.rego | 197 + Testing/Unit/Rego/EXO/EXOConfig2_03_test.rego | 347 + Testing/Unit/Rego/EXO/EXOConfig2_04_test.rego | 335 + Testing/Unit/Rego/EXO/EXOConfig2_05_test.rego | 47 + Testing/Unit/Rego/EXO/EXOConfig2_06_test.rego | 106 + Testing/Unit/Rego/EXO/EXOConfig2_07_test.rego | 88 + Testing/Unit/Rego/EXO/EXOConfig2_08_test.rego | 35 + Testing/Unit/Rego/EXO/EXOConfig2_09_test.rego | 51 + Testing/Unit/Rego/EXO/EXOConfig2_10_test.rego | 51 + Testing/Unit/Rego/EXO/EXOConfig2_11_test.rego | 51 + Testing/Unit/Rego/EXO/EXOConfig2_12_test.rego | 136 + Testing/Unit/Rego/EXO/EXOConfig2_13_test.rego | 49 + Testing/Unit/Rego/EXO/EXOConfig2_14_test.rego | 51 + Testing/Unit/Rego/EXO/EXOConfig2_15_test.rego | 51 + Testing/Unit/Rego/EXO/EXOConfig2_16_test.rego | 35 + Testing/Unit/Rego/EXO/EXOConfig2_17_test.rego | 51 + .../OneDrive/OneDriveConfig2_01_test.rego | 44 + .../OneDrive/OneDriveConfig2_02_test.rego | 105 + .../OneDrive/OneDriveConfig2_03_test.rego | 44 + .../OneDrive/OneDriveConfig2_04_test.rego | 69 + .../OneDrive/OneDriveConfig2_05_test.rego | 44 + .../OneDrive/OneDriveConfig2_06_test.rego | 19 + .../OneDrive/OneDriveConfig2_07_test.rego | 19 + .../PowerPlatformConfig2_01_test.rego | 40 + .../PowerPlatformConfig2_02_test.rego | 293 + .../PowerPlatformConfig2_03_test.rego | 60 + .../PowerPlatformConfig2_04_test.rego | 19 + .../Sharepoint/SharepointConfig2_01_test.rego | 44 + .../Sharepoint/SharepointConfig2_02_test.rego | 44 + .../Sharepoint/SharepointConfig2_03_test.rego | 19 + .../Sharepoint/SharepointConfig2_04_test.rego | 104 + .../Sharepoint/SharepointConfig2_05_test.rego | 59 + .../Unit/Rego/Teams/TeamsConfig2_01_test.rego | 112 + .../Unit/Rego/Teams/TeamsConfig2_02_test.rego | 117 + .../Unit/Rego/Teams/TeamsConfig2_03_test.rego | 179 + .../Unit/Rego/Teams/TeamsConfig2_04_test.rego | 332 + .../Unit/Rego/Teams/TeamsConfig2_05_test.rego | 290 + .../Unit/Rego/Teams/TeamsConfig2_06_test.rego | 94 + .../Unit/Rego/Teams/TeamsConfig2_07_test.rego | 222 + .../Unit/Rego/Teams/TeamsConfig2_08_test.rego | 355 + .../Unit/Rego/Teams/TeamsConfig2_09_test.rego | 165 + .../Unit/Rego/Teams/TeamsConfig2_10_test.rego | 71 + .../Unit/Rego/Teams/TeamsConfig2_11_test.rego | 51 + .../Unit/Rego/Teams/TeamsConfig2_12_test.rego | 35 + .../Unit/Rego/Teams/TeamsConfig2_13_test.rego | 51 + images/scuba-architecture.png | Bin 0 -> 178045 bytes sample-report/BaselineReports.html | Bin 0 -> 12774 bytes .../IndividualReports/AADReport.html | Bin 0 -> 30752 bytes .../IndividualReports/DefenderReport.html | Bin 0 -> 41176 bytes .../IndividualReports/EXOReport.html | Bin 0 -> 34518 bytes .../IndividualReports/OneDriveReport.html | Bin 0 -> 13712 bytes .../IndividualReports/SharePointReport.html | Bin 0 -> 13854 bytes .../IndividualReports/TeamsReport.html | Bin 0 -> 24856 bytes sample-report/IndividualReports/cisa_logo.png | Bin 0 -> 329167 bytes sample-report/ProviderSettingsExport.json | 12338 ++++++++++++++++ sample-report/TestResults.csv | 191 + sample-report/TestResults.json | 2465 +++ utils/RegoCachedProviderTesting.ps1 | 35 + utils/RunSCuBA.ps1 | 35 + utils/ScubaGearSupport.ps1 | 119 + utils/UninstallModules.ps1 | 60 + 130 files changed, 34418 insertions(+), 859 deletions(-) create mode 100644 .gitattributes create mode 100644 PowerShell/ScubaGear/Dependencies.ps1 create mode 100644 PowerShell/ScubaGear/Modules/Connection/ConnectHelpers.psm1 create mode 100644 PowerShell/ScubaGear/Modules/Providers/ProviderHelpers/CommandTracker.psm1 create mode 100644 PowerShell/ScubaGear/Modules/Providers/ProviderHelpers/SPOSiteHelper.psm1 create mode 100644 PowerShell/ScubaGear/RequiredVersions.ps1 create mode 100644 PowerShell/ScubaGear/ScubaGear.psm1 create mode 100644 Testing/Functional/Auto/ExtremeTest.txt create mode 100644 Testing/Functional/Auto/MinimumTest.txt create mode 100644 Testing/Functional/Auto/SimpleTest.txt create mode 100644 Testing/Functional/RegoCachedProviderTesting.ps1 create mode 100644 Testing/RunFunctionalTests.ps1 create mode 100644 Testing/RunUnitTests.ps1 create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_05_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_06_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_07_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_08_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_09_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_10_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_11_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_12_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_13_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_14_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_15_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_16_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_17_test.rego create mode 100644 Testing/Unit/Rego/AAD/AADConfig2_18_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_05_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_06_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_07_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_08_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_09_test.rego create mode 100644 Testing/Unit/Rego/Defender/DefenderConfig2_10_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_05_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_06_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_07_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_08_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_09_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_10_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_11_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_12_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_13_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_14_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_15_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_16_test.rego create mode 100644 Testing/Unit/Rego/EXO/EXOConfig2_17_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_05_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_06_test.rego create mode 100644 Testing/Unit/Rego/OneDrive/OneDriveConfig2_07_test.rego create mode 100644 Testing/Unit/Rego/PowerPlatform/PowerPlatformConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/PowerPlatform/PowerPlatformConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/PowerPlatform/PowerPlatformConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/PowerPlatform/PowerPlatformConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/Sharepoint/SharepointConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/Sharepoint/SharepointConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/Sharepoint/SharepointConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/Sharepoint/SharepointConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/Sharepoint/SharepointConfig2_05_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_01_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_02_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_03_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_04_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_05_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_06_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_07_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_08_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_09_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_10_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_11_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_12_test.rego create mode 100644 Testing/Unit/Rego/Teams/TeamsConfig2_13_test.rego create mode 100644 images/scuba-architecture.png create mode 100644 sample-report/BaselineReports.html create mode 100644 sample-report/IndividualReports/AADReport.html create mode 100644 sample-report/IndividualReports/DefenderReport.html create mode 100644 sample-report/IndividualReports/EXOReport.html create mode 100644 sample-report/IndividualReports/OneDriveReport.html create mode 100644 sample-report/IndividualReports/SharePointReport.html create mode 100644 sample-report/IndividualReports/TeamsReport.html create mode 100644 sample-report/IndividualReports/cisa_logo.png create mode 100644 sample-report/ProviderSettingsExport.json create mode 100644 sample-report/TestResults.csv create mode 100644 sample-report/TestResults.json create mode 100644 utils/RegoCachedProviderTesting.ps1 create mode 100644 utils/RunSCuBA.ps1 create mode 100644 utils/ScubaGearSupport.ps1 create mode 100644 utils/UninstallModules.ps1 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000..400ef44334 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +**/*.psd1 diff \ No newline at end of file diff --git a/.github/workflows/run_opa_tests.yaml b/.github/workflows/run_opa_tests.yaml index a86f1ba557..0f57646cc6 100644 --- a/.github/workflows/run_opa_tests.yaml +++ b/.github/workflows/run_opa_tests.yaml @@ -2,6 +2,7 @@ name: Run OPA Tests on: # Run tests on each commit, newly opened/reopened PR, and # PR review submission (e.g. approval) + workflow_dispatch: push: paths: - "**.rego" @@ -30,4 +31,4 @@ jobs: run: opa check --strict Rego Testing - name: Run OPA Tests - run: opa test Rego/*.rego Testing/**/*.rego -v + run: opa test Rego/*.rego Testing/Unit/Rego/**/*.rego -v diff --git a/.gitignore b/.gitignore index 7bcac1dd16..e0a6439f24 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,15 @@ /output /example /M365Baseline* -/Reports* \ No newline at end of file +/Reports* +/utils/Reports* +/utils/output +/utils/M365Baseline* + +# IDE +/.vscode + +# Reports +**/M365BaselineConformance* +/Testing/Functional/Reports* +/Testing/Functional/Archive* \ No newline at end of file diff --git a/AllowBasicAuthentication.ps1 b/AllowBasicAuthentication.ps1 index 3eadc07a22..edf93c27f8 100644 --- a/AllowBasicAuthentication.ps1 +++ b/AllowBasicAuthentication.ps1 @@ -1,29 +1,57 @@ #Requires -RunAsAdministrator -# Run this script to enable basic authentication on your local desktop if you get an error when connecting to Exchange Online. -# See README file Troubleshooting section for details. -# -# This script requires administrative privileges on your local desktop and updates a registry key. -# -$regPath = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client' -$regKey = 'AllowBasic' +<# + .SYNOPSIS + Set Registry to allow basic authentication for WinRM Client + + .DESCRIPTION + Run this script to enable basic authentication on your local desktop if you get an error when connecting to Exchange Online. + + .NOTES + See README file Troubleshooting section for details. + This script requires administrative privileges on your local desktop and updates a registry key. +#> + +function Test-RegistryKey { + <# + .SYNOPSIS + Test if registry key exists + #> + param ( + [parameter (Mandatory = $true)] + [ValidateNotNullOrEmpty()]$Path, + [parameter (Mandatory = $true)] + [ValidateNotNullOrEmpty()]$Key + ) -if (Test-Path -LiteralPath $regPath){ try { - $allowBasic = Get-ItemPropertyValue -Path $regPath -Name $regKey -ErrorAction Stop - } - catch [System.Management.Automation.PSArgumentException]{ - Write-Error -Message "Key, $regKey, was not found" + Get-ItemProperty -Path $Path -Name $Key -ErrorAction Stop | Out-Null + return $true } - catch{ - Write-Error -Message "Unexpected error occured attempting to get registry key, $regKey." + catch { + return $false } +} - if ($allowBasic -ne '1'){ +$regPath = 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client' +$regKey = 'AllowBasic' + +if (-Not $(Test-Path -LiteralPath $regPath)) { + New-Item -Path $regPath -Force | Out-Null + New-ItemProperty -Path $regPath -Name $regKey | Out-Null +} elseif (-Not $(Test-RegistryKey -Path $regPath -Key $regKey)) { + New-ItemProperty -Path $regPath -Name $regKey | Out-Null +} + +try { + $allowBasic = Get-ItemPropertyValue -Path $regPath -Name $regKey -ErrorAction Stop + + if ($allowBasic -ne '1') { Set-ItemProperty -Path $regPath -Name $regKey -Type DWord -Value '1' } } -else { - Write-Error -Message "Registry path not found: $regPath" +catch { + Write-Error -Message "Unexpected error occured attempting to update registry key, $regKey." } + diff --git a/PowerShell/ScubaGear/Dependencies.ps1 b/PowerShell/ScubaGear/Dependencies.ps1 new file mode 100644 index 0000000000..1616e37d99 --- /dev/null +++ b/PowerShell/ScubaGear/Dependencies.ps1 @@ -0,0 +1,41 @@ +#Requires -Version 5.1 +<# + .SYNOPSIS + This script verifies the required Powershell modules used by the + assessment tool are installed. + .DESCRIPTION + Verifies a supported version of the modules required to support SCuBAGear are installed. +#> + +$RequiredModulesPath = Join-Path -Path $PSScriptRoot -ChildPath "RequiredVersions.ps1" +if (Test-Path -Path $RequiredModulesPath){ + . $RequiredModulesPath +} + +if (!$ModuleList){ + throw "Required modules list is required." +} + +foreach ($Module in $ModuleList) { + $InstalledModuleVersions = Get-Module -ListAvailable -Name $($Module.ModuleName) + $FoundAcceptableVersion = $false + + foreach ($ModuleVersion in $InstalledModuleVersions){ + + if (($ModuleVersion.Version -ge $Module.ModuleVersion) -and ($ModuleVersion.Version -le $Module.MaximumVersion)){ + $FoundAcceptableVersion = $true + break; + } + } + + if (-not $FoundAcceptableVersion) { + throw [System.IO.FileNotFoundException] "No acceptable installed version found for module: $($Module.ModuleName) + Required Min Version: $($Module.ModuleVersion) | Max Version: $($Module.MaximumVersion) + Run Get-InstalledModule to see a list of currently installed modules + Run SetUp.ps1 or Install-Module $($Module.ModuleName) -force to install the latest version of $($Module.ModuleName)" + } +} + + + + diff --git a/PowerShell/ScubaGear/Modules/Connection/ConnectHelpers.psm1 b/PowerShell/ScubaGear/Modules/Connection/ConnectHelpers.psm1 new file mode 100644 index 0000000000..ff4d63e269 --- /dev/null +++ b/PowerShell/ScubaGear/Modules/Connection/ConnectHelpers.psm1 @@ -0,0 +1,33 @@ +function Connect-EXOHelper { + <# + .Description + This function is used for assisting in connecting to different M365 Environments for EXO. + .Functionality + Internal + #> + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [ValidateSet("commercial", "gcc", "gcchigh", "dod", IgnoreCase = $false)] + [string] + $M365Environment + ) + switch ($M365Environment) { + {($_ -eq "commercial") -or ($_ -eq "gcc")} { + Connect-ExchangeOnline -ShowBanner:$false -ErrorAction "Stop" | Out-Null + } + "gcchigh" { + Connect-ExchangeOnline -ShowBanner:$false -ExchangeEnvironmentName "O365USGovGCCHigh" -ErrorAction "Stop" | Out-Null + } + "dod" { + Connect-ExchangeOnline -ShowBanner:$false -ExchangeEnvironmentName "O365USGovDoD" -ErrorAction "Stop" | Out-Null + } + default { + throw "Unsupported or invalid M365Environment argument" + } + } +} + +Export-ModuleMember -Function @( + 'Connect-EXOHelper' +) \ No newline at end of file diff --git a/PowerShell/ScubaGear/Modules/Connection/Connection.psm1 b/PowerShell/ScubaGear/Modules/Connection/Connection.psm1 index fcbfb58437..256d8b040f 100644 --- a/PowerShell/ScubaGear/Modules/Connection/Connection.psm1 +++ b/PowerShell/ScubaGear/Modules/Connection/Connection.psm1 @@ -7,89 +7,271 @@ function Connect-Tenant { .Functionality Internal #> + [CmdletBinding()] param ( - [Parameter(Mandatory=$true)] + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [ValidateSet("teams", "exo", "defender", "aad", "powerplatform", "sharepoint", "onedrive", IgnoreCase = $false)] [string[]] $ProductNames, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [ValidateSet("commercial", "gcc", "gcchigh", "dod", IgnoreCase = $false)] [string] - $Endpoint + $M365Environment ) + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath "ConnectHelpers.psm1") # Prevent duplicate sign ins $EXOAuthRequired = $true $SPOAuthRequired = $true $AADAuthRequired = $true + $ProdAuthFailed = @() + $N = 0 $Len = $ProductNames.Length foreach ($Product in $ProductNames) { $N += 1 $Percent = $N*100/$Len - Write-Progress -Activity "Authenticating to each service" -Status "Authenticating to $($Product); $($n) of $($Len) Products authenticated to." -PercentComplete $Percent - switch ($Product) { - {($_ -eq "exo") -or ($_ -eq "defender")} { - if ($EXOAuthRequired) { - Connect-ExchangeOnline -ShowBanner:$false | Out-Null - Write-Verbose "Defender will require a sign in every single run regardless of what the LogIn parameter is set" - $EXOAuthRequired = $false + $ProgressParams = @{ + 'Activity' = "Authenticating to each Product"; + 'Status' = "Authenticating to $($Product); $($N) of $($Len) Products authenticated to."; + 'PercentComplete' = $Percent; + } + Write-Progress @ProgressParams + try { + switch ($Product) { + "aad" { + $GraphScopes = ( + 'User.Read.All', + 'Policy.Read.All', + 'Organization.Read.All', + 'UserAuthenticationMethod.Read.All', + 'RoleManagement.Read.Directory', + 'GroupMember.Read.All', + 'Directory.Read.All' + ) + $GraphParams = @{ + 'Scopes' = $GraphScopes; + 'ErrorAction' = 'Stop'; + } + switch ($M365Environment) { + {($_ -eq "commercial") -or ($_ -eq "gcc")} { + # Sanity check + $GraphParams = @{ + 'Scopes' = $GraphScopes; + 'ErrorAction' = 'Stop'; + } + } + "gcchigh" { + $GraphParams = $GraphParams + @{'Environment' = "USGov";} + } + "dod" { + $GraphParams = $GraphParams + @{'Environment' = "USGovDoD";} + } + default { + throw "Unsupported or invalid M365Environment argument" + } + } + Connect-MgGraph @GraphParams | Out-Null + Select-MgProfile -Name "Beta" -ErrorAction "Stop" | Out-Null + $AADAuthRequired = $false } - } - "aad" { - Connect-MgGraph -Scopes User.Read.All, Policy.Read.All, Organization.Read.All, UserAuthenticationMethod.Read.All, RoleManagement.Read.Directory, GroupMember.Read.All, Policy.ReadWrite.AuthenticationMethod, Directory.Read.All -ErrorAction Stop | Out-Null - Select-MgProfile Beta | Out-Null - $AADAuthRequired = $false - } - "powerplatform"{ - if (!$Endpoint) { - Write-Output "Power Platform needs an endpoint please specify one as a script arg" + {($_ -eq "exo") -or ($_ -eq "defender")} { + if ($EXOAuthRequired) { + # Moved switch to Connect-Helpers for Defender Provider use + Connect-EXOHelper -M365Environment $M365Environment + Write-Verbose "Defender will require a sign in every single run regardless of what the LogIn parameter is set" + $EXOAuthRequired = $false + } } - else { - Add-PowerAppsAccount -Endpoint $Endpoint | Out-Null + "powerplatform" { + $AddPowerAppsParams = @{ + 'ErrorAction' = 'Stop'; + } + switch ($M365Environment) { + "commercial" { + $AddPowerAppsParams = $AddPowerAppsParams + @{'Endpoint'='prod';} + } + "gcc" { + $AddPowerAppsParams = $AddPowerAppsParams + @{'Endpoint'='usgov';} + } + "gcchigh" { + $AddPowerAppsParams = $AddPowerAppsParams + @{'Endpoint'='usgovhigh';} + } + "dod" { + $AddPowerAppsParams = $AddPowerAppsParams + @{'Endpoint'='dod';} + } + default { + throw "Unsupported or invalid M365Environment argument" + } + } + Add-PowerAppsAccount @AddPowerAppsParams | Out-Null } - } - {($_ -eq "onedrive") -or ($_ -eq "sharepoint")} { - if ($AADAuthRequired) { - Connect-MgGraph | Out-Null - Select-MgProfile Beta | Out-Null - $AADAuthRequired = $false + {($_ -eq "onedrive") -or ($_ -eq "sharepoint")} { + if ($AADAuthRequired) { + $LimitedGraphParams = @{ + 'ErrorAction' = 'Stop'; + } + switch ($M365Environment) { + {($_ -eq "commercial") -or ($_ -eq "gcc")} { + $LimitedGraphParams = @{ + 'ErrorAction' = 'Stop'; + } + } + "gcchigh" { + $LimitedGraphParams = $LimitedGraphParams + @{'Environment' = "USGov";} + } + "dod" { + $LimitedGraphParams = $LimitedGraphParams + @{'Environment' = "USGovDoD";} + } + default { + throw "Unsupported or invalid M365Environment argument" + } + } + Connect-MgGraph @LimitedGraphParams | Out-Null + Select-MgProfile -Name "Beta" -ErrorAction "Stop" | Out-Null + $AADAuthRequired = $false + } + if ($SPOAuthRequired) { + $InitialDomain = (Get-MgOrganization).VerifiedDomains | Where-Object {$_.isInitial} + $InitialDomainPrefix = $InitialDomain.Name.split(".")[0] + $SPOParams = @{ + 'ErrorAction' = 'Stop'; + } + switch ($M365Environment) { + {($_ -eq "commercial") -or ($_ -eq "gcc")} { + $SPOParams = $SPOParams + @{ + 'Url'= "https://$($InitialDomainPrefix)-admin.sharepoint.com"; + } + } + "gcchigh" { + $SPOParams = $SPOParams + @{ + 'Url'= "https://$($InitialDomainPrefix)-admin.sharepoint.us"; + 'Region' = "ITAR"; + } + } + "dod" { + $SPOParams = $SPOParams + @{ + 'Url'= "https://$($InitialDomainPrefix)-admin.sharepoint-mil.us"; + 'Region' = "ITAR"; + } + } + default { + throw "Unsupported or invalid M365Environment argument" + } + } + Connect-SPOService @SPOParams | Out-Null + $SPOAuthRequired = $false + } } - if ($SPOAuthRequired) { - $InitialDomain = (Get-MgOrganization).VerifiedDomains | Where-Object {$_.isInitial} - $InitialDomainPrefix = $InitialDomain.Name.split(".")[0] - Connect-SPOService -Url "https://$($InitialDomainPrefix)-admin.sharepoint.com" | Out-Null - $SPOAuthRequired = $false + "teams" { + $TeamsParams = @{'ErrorAction'= 'Stop'} + switch ($M365Environment) { + {($_ -eq "commercial") -or ($_ -eq "gcc")} { + $TeamsParams = @{'ErrorAction'= 'Stop'} # sanity check + } + "gcchigh" { + $TeamsParams = $TeamsParams + @{'TeamsEnvironmentName'= 'TeamsGCCH';} + } + "dod" { + $TeamsParams = $TeamsParams + @{'TeamsEnvironmentName'= 'TeamsDOD';} + } + default { + throw "Unsupported or invalid M365Environment argument" + } + } + Connect-MicrosoftTeams @TeamsParams | Out-Null + } + default { + Write-Error -Message "Invalid ProductName argument" } - } - "teams" { - Connect-MicrosoftTeams | Out-Null - } - default { - Write-Error -Message "Invalid ProductName argument" } } + catch { + Write-Error "Error establishing a connection with $($Product). $($_)" + $ProdAuthFailed += $Product + Write-Warning "$($Product) will be omitted from the output because of failed authentication" + } } Write-Progress -Activity "Authenticating to each service" -Status "Ready" -Completed + $ProdAuthFailed } -function Disconnect-Tenant { +function Disconnect-SCuBATenant { <# - .Description - This function disconnects the various PowerShell module sessions from the - M365 Tenant. Useful to disconnect then connect to other M365 tenants - Currently Disconect-MgGraph is buggy and may not disconnect properly. + .SYNOPSIS + Disconnect all active M365 connection sessions made by ScubaGear + .DESCRIPTION + Forces disconnect of all outstanding open sessions associated with + M365 product APIs within the current PowerShell session. + Best used after an ScubaGear run to ensure a new tenant connection is + used for future ScubaGear runs. + .Parameter ProductNames + A list of one or more M365 shortened product names this function will disconnect from. By default this function will disconnect from all possible products ScubaGear can run against. + .EXAMPLE + Disconnect-SCuBATenant + .EXAMPLE + Disconnect-SCuBATenant -ProductNames teams + .EXAMPLE + Disconnect-SCuBATenant -ProductNames aad, exo .Functionality Public #> - Disconnect-MicrosoftTeams # Teams - Disconnect-MgGraph # AAD - Disconnect-ExchangeOnline -Confirm:$false -InformationAction Ignore -ErrorAction SilentlyContinue | Out-Null # Exchange and Defender - Remove-PowerAppsAccount # Power Platform - Disconnect-SPOService # OneDrive and Sharepoint + [CmdletBinding()] + param( + [ValidateSet("aad", "defender", "exo", "onedrive","powerplatform", "sharepoint", "teams", IgnoreCase = $false)] + [string[]] + $ProductNames = @("aad", "defender", "exo", "onedrive", "powerplatform", "sharepoint", "teams") + ) + $ErrorActionPreference = "SilentlyContinue" + + try { + $N = 0 + $Len = $ProductNames.Length + + foreach ($Product in $ProductNames) { + $N += 1 + $Percent = $N*100/$Len + Write-Progress -Activity "Disconnecting from each service" -Status "Disconnecting from $($Product); $($n) of $($Len) disconnected." -PercentComplete $Percent + Write-Verbose "Disconnecting from $Product." + if (($Product -eq "aad") -or ($Product -eq "onedrive") -or ($Product -eq "sharepoint")) { + Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null + + if($Product -eq "sharepoint") { + Disconnect-SPOService -ErrorAction SilentlyContinue + } + } + elseif ($Product -eq "teams") { + Disconnect-MicrosoftTeams -Confirm:$false -ErrorAction SilentlyContinue + } + elseif ($Product -eq "powerplatform") { + Remove-PowerAppsAccount -ErrorAction SilentlyContinue -WarningAction SilentlyContinue + } + elseif (($Product -eq "exo") -or ($Product -eq "defender")) { + Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue -InformationAction SilentlyContinue | Out-Null + } + else { + Write-Warning "Product $Product not recognized, skipping..." + } + } + Write-Progress -Activity "Disconnecting from each service" -Status "Done" -Completed + + } catch [System.InvalidOperationException] { + # Suppress error due to disconnect from service with no active connection + continue + } catch { + Write-Error "ERRROR: Could not disconnect from $Product`n$($Error[0]): " + } finally { + $ErrorActionPreference = "Continue" + } + } Export-ModuleMember -Function @( 'Connect-Tenant', - 'Disconnect-Tenant' + 'Disconnect-SCuBATenant' ) diff --git a/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 b/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 index 9b4830398f..27657c9a47 100644 --- a/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 +++ b/PowerShell/ScubaGear/Modules/CreateReport/CreateReport.psm1 @@ -17,50 +17,77 @@ function New-Report { [string] $FullName, - # The location to save the html report in. Defaults to current directory. + # The location to save the html report in. [Parameter(Mandatory=$true)] + [ValidateScript({Test-Path -PathType Container $_})] [string] $IndividualReportPath, - # The location to save the html report in. Defaults to current directory. + # The location to save the html report in. [Parameter(Mandatory=$true)] + [ValidateScript({Test-Path -PathType Container $_})] [string] - $OutPath + $OutPath, + + [Parameter(Mandatory=$true)] + [string] + $OutProviderFileName, + + [Parameter(Mandatory=$true)] + [string] + $OutRegoFileName ) $FileName = Join-Path -Path $PSScriptRoot -ChildPath "BaselineTitles.json" $AllTitles = Get-Content $FileName | ConvertFrom-Json $Titles = $AllTitles.$BaselineName -$FileName = Join-Path -Path $OutPath -ChildPath "TestResults.json" -$TestResults = Get-Content $FileName | ConvertFrom-Json - -$FileName = Join-Path -Path $OutPath -ChildPath "ProviderSettingsExport.json" +$FileName = Join-Path -Path $OutPath -ChildPath "$($OutProviderFileName).json" $SettingsExport = Get-Content $FileName | ConvertFrom-Json +$FileName = Join-Path -Path $OutPath -ChildPath "$($OutRegoFileName).json" +$TestResults = Get-Content $FileName | ConvertFrom-Json + $Fragments = @() $MetaData += [pscustomobject]@{ - "Tenant Name"= $SettingsExport.tenant_details.DisplayName; - "Report Date"=$SettingsExport.date; - "Baseline Version"=$SettingsExport.baseline_version; - "Module Version"=$SettingsExport.module_version + "Tenant Display Name" = $SettingsExport.tenant_details.DisplayName; + "Report Date" = $SettingsExport.date; + "Baseline Version" = $SettingsExport.baseline_version; + "Module Version" = $SettingsExport.module_version } -$Fragments += $MetaData | ConvertTo-HTML -Fragment - +$MetaDataTable = $MetaData | ConvertTo-HTML -Fragment +$MetaDataTable = $MetaDataTable -replace '^(.*?)