|
|
@@ -4,6 +4,7 @@
|
|
|
|
.DESCRIPTION
|
|
|
|
.DESCRIPTION
|
|
|
|
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters.
|
|
|
|
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters.
|
|
|
|
It allows auditing of various configurations and settings within a Microsoft 365 environment in alignment with CIS benchmarks designated "Automatic".
|
|
|
|
It allows auditing of various configurations and settings within a Microsoft 365 environment in alignment with CIS benchmarks designated "Automatic".
|
|
|
|
|
|
|
|
Supports selection of CIS benchmark definitions version (default is 4.0.0).
|
|
|
|
.PARAMETER TenantAdminUrl
|
|
|
|
.PARAMETER TenantAdminUrl
|
|
|
|
The URL of the tenant admin. If not specified, none of the SharePoint Online tests will run.
|
|
|
|
The URL of the tenant admin. If not specified, none of the SharePoint Online tests will run.
|
|
|
|
.PARAMETER DomainName
|
|
|
|
.PARAMETER DomainName
|
|
|
@@ -12,7 +13,7 @@
|
|
|
|
.PARAMETER ELevel
|
|
|
|
.PARAMETER ELevel
|
|
|
|
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
|
|
|
|
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
|
|
|
|
.PARAMETER ProfileLevel
|
|
|
|
.PARAMETER ProfileLevel
|
|
|
|
Specifies the profile level (L1 or L2) for the audit. This parameter is mandatory, but only when ELevel is selected. Otherwise it is not required.
|
|
|
|
Specifies the profile level (L1 or L2) for the audit. This parameter is mandatory, but only when ELevel is selected. Otherwise, it is not required.
|
|
|
|
.PARAMETER IncludeIG1
|
|
|
|
.PARAMETER IncludeIG1
|
|
|
|
If specified, includes tests where IG1 is true.
|
|
|
|
If specified, includes tests where IG1 is true.
|
|
|
|
.PARAMETER IncludeIG2
|
|
|
|
.PARAMETER IncludeIG2
|
|
|
@@ -39,6 +40,8 @@
|
|
|
|
If specified, the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.
|
|
|
|
If specified, the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.
|
|
|
|
.PARAMETER AuthParams
|
|
|
|
.PARAMETER AuthParams
|
|
|
|
Specifies an authentication object containing parameters for application-based authentication. If provided, this will be used for connecting to services.
|
|
|
|
Specifies an authentication object containing parameters for application-based authentication. If provided, this will be used for connecting to services.
|
|
|
|
|
|
|
|
.PARAMETER Version
|
|
|
|
|
|
|
|
Specifies the CIS benchmark definitions version to use. Default is 4.0.0. Valid values are "3.0.0" or "4.0.0".
|
|
|
|
.EXAMPLE
|
|
|
|
.EXAMPLE
|
|
|
|
PS> Invoke-M365SecurityAudit
|
|
|
|
PS> Invoke-M365SecurityAudit
|
|
|
|
# Performs a security audit using default parameters.
|
|
|
|
# Performs a security audit using default parameters.
|
|
|
@@ -51,6 +54,9 @@
|
|
|
|
.EXAMPLE
|
|
|
|
.EXAMPLE
|
|
|
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
|
|
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
|
|
|
# Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
|
|
|
# Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
|
|
|
|
|
|
|
.EXAMPLE
|
|
|
|
|
|
|
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -Version "3.0.0"
|
|
|
|
|
|
|
|
# Performs a security audit using the CIS benchmark definitions version 3.0.0.
|
|
|
|
.EXAMPLE
|
|
|
|
.EXAMPLE
|
|
|
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
|
|
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
|
|
|
PS> Export-M365SecurityAuditTable -AuditResults $auditResults -ExportPath "C:\temp" -ExportOriginalTests -ExportAllTests
|
|
|
|
PS> Export-M365SecurityAuditTable -AuditResults $auditResults -ExportPath "C:\temp" -ExportOriginalTests -ExportAllTests
|
|
|
@@ -89,9 +95,10 @@
|
|
|
|
.LINK
|
|
|
|
.LINK
|
|
|
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
|
|
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
|
|
|
#>
|
|
|
|
#>
|
|
|
|
|
|
|
|
|
|
|
|
function Invoke-M365SecurityAudit {
|
|
|
|
function Invoke-M365SecurityAudit {
|
|
|
|
# Add confirm to high
|
|
|
|
# Add confirm to high
|
|
|
|
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High" , DefaultParameterSetName = 'Default')]
|
|
|
|
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High' , DefaultParameterSetName = 'Default')]
|
|
|
|
[OutputType([CISAuditResult[]])]
|
|
|
|
[OutputType([CISAuditResult[]])]
|
|
|
|
param (
|
|
|
|
param (
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
|
|
|
@@ -101,21 +108,21 @@ function Invoke-M365SecurityAudit {
|
|
|
|
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
|
|
|
|
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
|
|
|
|
[string]$DomainName,
|
|
|
|
[string]$DomainName,
|
|
|
|
# E-Level with optional ProfileLevel selection
|
|
|
|
# E-Level with optional ProfileLevel selection
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = "Specifies the E-Level (E3 or E5) for the audit.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = 'Specifies the E-Level (E3 or E5) for the audit.')]
|
|
|
|
[ValidateSet('E3', 'E5')]
|
|
|
|
[ValidateSet('E3', 'E5')]
|
|
|
|
[string]$ELevel,
|
|
|
|
[string]$ELevel,
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = "Specifies the profile level (L1 or L2) for the audit.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = 'Specifies the profile level (L1 or L2) for the audit.')]
|
|
|
|
[ValidateSet('L1', 'L2')]
|
|
|
|
[ValidateSet('L1', 'L2')]
|
|
|
|
[string]$ProfileLevel,
|
|
|
|
[string]$ProfileLevel,
|
|
|
|
# IG Filters, one at a time
|
|
|
|
# IG Filters, one at a time
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter', HelpMessage = "Includes tests where IG1 is true.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter', HelpMessage = 'Includes tests where IG1 is true.')]
|
|
|
|
[switch]$IncludeIG1,
|
|
|
|
[switch]$IncludeIG1,
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter', HelpMessage = "Includes tests where IG2 is true.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter', HelpMessage = 'Includes tests where IG2 is true.')]
|
|
|
|
[switch]$IncludeIG2,
|
|
|
|
[switch]$IncludeIG2,
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter', HelpMessage = "Includes tests where IG3 is true.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter', HelpMessage = 'Includes tests where IG3 is true.')]
|
|
|
|
[switch]$IncludeIG3,
|
|
|
|
[switch]$IncludeIG3,
|
|
|
|
# Inclusion of specific recommendation numbers
|
|
|
|
# Inclusion of specific recommendation numbers
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter', HelpMessage = "Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter', HelpMessage = 'Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers.')]
|
|
|
|
[ValidateSet(
|
|
|
|
[ValidateSet(
|
|
|
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
|
|
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
|
|
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
|
|
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
|
|
@@ -127,7 +134,7 @@ function Invoke-M365SecurityAudit {
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
[string[]]$IncludeRecommendation,
|
|
|
|
[string[]]$IncludeRecommendation,
|
|
|
|
# Exclusion of specific recommendation numbers
|
|
|
|
# Exclusion of specific recommendation numbers
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter', HelpMessage = "Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers.")]
|
|
|
|
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter', HelpMessage = 'Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers.')]
|
|
|
|
[ValidateSet(
|
|
|
|
[ValidateSet(
|
|
|
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
|
|
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
|
|
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
|
|
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
|
|
@@ -139,24 +146,27 @@ function Invoke-M365SecurityAudit {
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
[string[]]$SkipRecommendation,
|
|
|
|
[string[]]$SkipRecommendation,
|
|
|
|
# Common parameters for all parameter sets
|
|
|
|
# Common parameters for all parameter sets
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.')]
|
|
|
|
[ValidateSet(
|
|
|
|
[ValidateSet(
|
|
|
|
'GoogleDrive', 'ShareFile', 'Box', 'DropBox', 'Egnyte'
|
|
|
|
'GoogleDrive', 'ShareFile', 'Box', 'DropBox', 'Egnyte'
|
|
|
|
)]
|
|
|
|
)]
|
|
|
|
[string[]]$ApprovedCloudStorageProviders = @(),
|
|
|
|
[string[]]$ApprovedCloudStorageProviders = @(),
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.')]
|
|
|
|
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
|
|
|
|
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
|
|
|
|
[string[]]$ApprovedFederatedDomains,
|
|
|
|
[string[]]$ApprovedFederatedDomains,
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not establish a connection to Microsoft 365 services.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies that the cmdlet will not establish a connection to Microsoft 365 services.')]
|
|
|
|
[switch]$DoNotConnect,
|
|
|
|
[switch]$DoNotConnect,
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not disconnect from Microsoft 365 services after execution.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies that the cmdlet will not disconnect from Microsoft 365 services after execution.')]
|
|
|
|
[switch]$DoNotDisconnect,
|
|
|
|
[switch]$DoNotDisconnect,
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not check for the presence of required modules.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies that the cmdlet will not check for the presence of required modules.')]
|
|
|
|
[switch]$NoModuleCheck,
|
|
|
|
[switch]$NoModuleCheck,
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies that the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.')]
|
|
|
|
[switch]$DoNotConfirmConnections,
|
|
|
|
[switch]$DoNotConfirmConnections,
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies an authentication object containing parameters for application-based authentication.")]
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = 'Specifies an authentication object containing parameters for application-based authentication.')]
|
|
|
|
[CISAuthenticationParameters]$AuthParams
|
|
|
|
[CISAuthenticationParameters]$AuthParams,
|
|
|
|
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Specifies the CIS benchmark definitions version to use. Default is 4.0.0. Valid values are '3.0.0' or '4.0.0'.")]
|
|
|
|
|
|
|
|
[ValidateSet('3.0.0', '4.0.0')]
|
|
|
|
|
|
|
|
[string]$Version = '4.0.0'
|
|
|
|
)
|
|
|
|
)
|
|
|
|
Begin {
|
|
|
|
Begin {
|
|
|
|
if ($script:MaximumFunctionCount -lt 8192) {
|
|
|
|
if ($script:MaximumFunctionCount -lt 8192) {
|
|
|
@@ -171,15 +181,33 @@ function Invoke-M365SecurityAudit {
|
|
|
|
# Format the required modules list
|
|
|
|
# Format the required modules list
|
|
|
|
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
|
|
|
|
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
|
|
|
|
# Check and install required modules if necessary
|
|
|
|
# Check and install required modules if necessary
|
|
|
|
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Modules: $requiredModulesFormatted", "Assert-ModuleAvailability")) {
|
|
|
|
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Modules: $requiredModulesFormatted", 'Assert-ModuleAvailability')) {
|
|
|
|
Write-Information "Checking for and installing required modules..."
|
|
|
|
Write-Information 'Checking for and installing required modules...'
|
|
|
|
foreach ($module in $requiredModules) {
|
|
|
|
foreach ($module in $requiredModules) {
|
|
|
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
|
|
|
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
# Load test definitions from CSV
|
|
|
|
# Load test definitions from CSV
|
|
|
|
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
|
|
|
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath 'helper\TestDefinitions.csv'
|
|
|
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
|
|
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
|
|
|
|
|
|
|
if ($Version -eq '4.0.0') {
|
|
|
|
|
|
|
|
$script:Version400 = $true
|
|
|
|
|
|
|
|
$testDefinitionsV4Path = Join-Path -Path $PSScriptRoot -ChildPath 'helper\TestDefinitions-v4.0.0.csv'
|
|
|
|
|
|
|
|
$testDefinitionsV4 = Import-Csv -Path $testDefinitionsV4Path
|
|
|
|
|
|
|
|
# Merge the definitions, prioritizing version 4.0.0
|
|
|
|
|
|
|
|
$mergedDefinitions = @{}
|
|
|
|
|
|
|
|
foreach ($test in $testDefinitions) {
|
|
|
|
|
|
|
|
$mergedDefinitions[$test.Rec] = $test
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($testV4 in $testDefinitionsV4) {
|
|
|
|
|
|
|
|
$mergedDefinitions[$testV4.Rec] = $testV4 # Overwrite if Rec exists
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
# Convert back to an array
|
|
|
|
|
|
|
|
$testDefinitions = $mergedDefinitions.Values
|
|
|
|
|
|
|
|
Write-Verbose "Total tests after merging: $(($testDefinitions).Count)"
|
|
|
|
|
|
|
|
$overwrittenTests = $testDefinitionsV4 | Where-Object { $testDefinitions[$_.Rec] }
|
|
|
|
|
|
|
|
Write-Verbose "Overwritten tests: $($overwrittenTests.Rec -join ', ')"
|
|
|
|
|
|
|
|
}
|
|
|
|
# Load the Test Definitions into the script scope for use in other functions
|
|
|
|
# Load the Test Definitions into the script scope for use in other functions
|
|
|
|
$script:TestDefinitionsObject = $testDefinitions
|
|
|
|
$script:TestDefinitionsObject = $testDefinitions
|
|
|
|
# Apply filters based on parameter sets
|
|
|
|
# Apply filters based on parameter sets
|
|
|
@@ -199,7 +227,7 @@ function Invoke-M365SecurityAudit {
|
|
|
|
$requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' }
|
|
|
|
$requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' }
|
|
|
|
$testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' }
|
|
|
|
$testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' }
|
|
|
|
if ($null -eq $testDefinitions) {
|
|
|
|
if ($null -eq $testDefinitions) {
|
|
|
|
throw "No tests to run as no SharePoint Online tests are available."
|
|
|
|
throw 'No tests to run as no SharePoint Online tests are available.'
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -213,15 +241,15 @@ function Invoke-M365SecurityAudit {
|
|
|
|
Process {
|
|
|
|
Process {
|
|
|
|
$allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
|
|
|
|
$allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
|
|
|
|
# Dynamically dot-source the test scripts
|
|
|
|
# Dynamically dot-source the test scripts
|
|
|
|
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
|
|
|
|
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath 'tests'
|
|
|
|
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
|
|
|
|
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter 'Test-*.ps1' |
|
|
|
|
Where-Object { $testsToLoad -contains $_.BaseName }
|
|
|
|
Where-Object { $testsToLoad -contains $_.BaseName }
|
|
|
|
$totalTests = $testFiles.Count
|
|
|
|
$totalTests = $testFiles.Count
|
|
|
|
$currentTestIndex = 0
|
|
|
|
$currentTestIndex = 0
|
|
|
|
# Establishing connections if required
|
|
|
|
# Establishing connections if required
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
|
|
|
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
|
|
|
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
|
|
|
|
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", 'Connect')) {
|
|
|
|
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')"
|
|
|
|
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')"
|
|
|
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections -SkipConfirmation:$DoNotConfirmConnections -AuthParams $AuthParams
|
|
|
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections -SkipConfirmation:$DoNotConfirmConnections -AuthParams $AuthParams
|
|
|
|
}
|
|
|
|
}
|
|
|
@@ -230,12 +258,12 @@ function Invoke-M365SecurityAudit {
|
|
|
|
Throw "Connection execution aborted: $_"
|
|
|
|
Throw "Connection execution aborted: $_"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
try {
|
|
|
|
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
|
|
|
|
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", 'Measure')) {
|
|
|
|
Write-Information "A total of $($totalTests) tests were selected to run..."
|
|
|
|
Write-Information "A total of $($totalTests) tests were selected to run..."
|
|
|
|
# Import the test functions
|
|
|
|
# Import the test functions
|
|
|
|
$testFiles | ForEach-Object {
|
|
|
|
$testFiles | ForEach-Object {
|
|
|
|
$currentTestIndex++
|
|
|
|
$currentTestIndex++
|
|
|
|
Write-Progress -Activity "Loading Test Scripts" -Status "Loading $($currentTestIndex) of $($totalTests): $($_.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
|
|
|
Write-Progress -Activity 'Loading Test Scripts' -Status "Loading $($currentTestIndex) of $($totalTests): $($_.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
|
|
|
Try {
|
|
|
|
Try {
|
|
|
|
# Dot source the test function
|
|
|
|
# Dot source the test function
|
|
|
|
. $_.FullName
|
|
|
|
. $_.FullName
|
|
|
@@ -250,7 +278,7 @@ function Invoke-M365SecurityAudit {
|
|
|
|
# Execute each test function from the prepared list
|
|
|
|
# Execute each test function from the prepared list
|
|
|
|
foreach ($testFunction in $testFiles) {
|
|
|
|
foreach ($testFunction in $testFiles) {
|
|
|
|
$currentTestIndex++
|
|
|
|
$currentTestIndex++
|
|
|
|
Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
|
|
|
Write-Progress -Activity 'Executing Tests' -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
|
|
|
$functionName = $testFunction.BaseName
|
|
|
|
$functionName = $testFunction.BaseName
|
|
|
|
Write-Information "Executing test function: $functionName"
|
|
|
|
Write-Information "Executing test function: $functionName"
|
|
|
|
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders -ApprovedFederatedDomains $ApprovedFederatedDomains
|
|
|
|
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders -ApprovedFederatedDomains $ApprovedFederatedDomains
|
|
|
@@ -261,7 +289,7 @@ function Invoke-M365SecurityAudit {
|
|
|
|
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
|
|
|
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
|
|
|
# Return all collected audit results
|
|
|
|
# Return all collected audit results
|
|
|
|
# Define the test numbers to check
|
|
|
|
# Define the test numbers to check
|
|
|
|
$TestNumbersToCheck = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
|
|
|
|
$TestNumbersToCheck = '1.1.1', '1.3.1', '6.1.2', '6.1.3', '7.3.4'
|
|
|
|
# Check for large details in the audit results
|
|
|
|
# Check for large details in the audit results
|
|
|
|
$exceedingTests = Get-ExceededLengthResultDetail -AuditResults $allAuditResults -TestNumbersToCheck $TestNumbersToCheck -ReturnExceedingTestsOnly -DetailsLengthLimit 30000
|
|
|
|
$exceedingTests = Get-ExceededLengthResultDetail -AuditResults $allAuditResults -TestNumbersToCheck $TestNumbersToCheck -ReturnExceedingTestsOnly -DetailsLengthLimit 30000
|
|
|
|
if ($exceedingTests.Count -gt 0) {
|
|
|
|
if ($exceedingTests.Count -gt 0) {
|
|
|
@@ -278,7 +306,7 @@ function Invoke-M365SecurityAudit {
|
|
|
|
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
|
|
|
|
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
finally {
|
|
|
|
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Disconnect")) {
|
|
|
|
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services: $($actualUniqueConnections -join ', ')", 'Disconnect')) {
|
|
|
|
# Clean up sessions
|
|
|
|
# Clean up sessions
|
|
|
|
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
|
|
|
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
|
|
|
}
|
|
|
|
}
|
|
|
|