Merge pull request #100 from CriticalSolutionsNetwork/Whatif-Bugfix
fix: whatif
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,6 +6,17 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added output type to functions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Whatif support for `Invoke-M365SecurityAudit`.
|
||||||
|
- Whatif module output and module install process.
|
||||||
|
|
||||||
|
## [0.1.7] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@@ -16,6 +27,12 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
- Enhanced `Invoke-M365SecurityAudit` to allow flexible inclusion and exclusion of specific recommendations, IG filters, and profile levels.
|
- Enhanced `Invoke-M365SecurityAudit` to allow flexible inclusion and exclusion of specific recommendations, IG filters, and profile levels.
|
||||||
- SupportsShoudProcess to also bypass connection checks in `Invoke-M365SecurityAudit` as well as Disconnect-M365Suite.
|
- SupportsShoudProcess to also bypass connection checks in `Invoke-M365SecurityAudit` as well as Disconnect-M365Suite.
|
||||||
|
|
||||||
|
## [0.1.6] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
## [0.1.5] - 2024-06-08
|
## [0.1.5] - 2024-06-08
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
@@ -4,7 +4,7 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
|||||||
|
|
||||||
|
|
||||||
<#
|
<#
|
||||||
$ver = "v0.1.6"
|
$ver = "v0.1.7"
|
||||||
git checkout main
|
git checkout main
|
||||||
git pull origin main
|
git pull origin main
|
||||||
git tag -a $ver -m "Release version $ver refactor Update"
|
git tag -a $ver -m "Release version $ver refactor Update"
|
||||||
|
@@ -1,33 +1,37 @@
|
|||||||
function Assert-ModuleAvailability {
|
function Assert-ModuleAvailability {
|
||||||
|
[OutputType([void]) ]
|
||||||
param(
|
param(
|
||||||
[string]$ModuleName,
|
[string]$ModuleName,
|
||||||
[string]$RequiredVersion,
|
[string]$RequiredVersion,
|
||||||
[string]$SubModuleName
|
[string[]]$SubModules = @()
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
||||||
|
|
||||||
if ($null -eq $module) {$auditResult.Profile
|
if ($null -eq $module) {
|
||||||
Write-Host "Installing $ModuleName module..."
|
Write-Information "Installing $ModuleName module..." -InformationAction Continue
|
||||||
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
||||||
}
|
}
|
||||||
elseif ($module.Version -lt [version]$RequiredVersion) {
|
elseif ($module.Version -lt [version]$RequiredVersion) {
|
||||||
Write-Host "Updating $ModuleName module to required version..."
|
Write-Information "Updating $ModuleName module to required version..." -InformationAction Continue
|
||||||
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Host "$ModuleName module is already at required version or newer."
|
Write-Information "$ModuleName module is already at required version or newer." -InformationAction Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($SubModuleName) {
|
if ($SubModules.Count -gt 0) {
|
||||||
Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
foreach ($subModule in $SubModules) {
|
||||||
}
|
Write-Information "Importing submodule $ModuleName.$subModule..." -InformationAction Continue
|
||||||
else {
|
Import-Module -Name "$ModuleName.$subModule" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Information "Importing module $ModuleName..." -InformationAction Continue
|
||||||
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "An error occurred with module $ModuleName`: $_"
|
Write-Warning "An error occurred with module $ModuleName`: $_"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Connect-M365Suite {
|
function Connect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory=$false)]
|
[Parameter(Mandatory=$false)]
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Disconnect-M365Suite {
|
function Disconnect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string[]]$RequiredConnections
|
[string[]]$RequiredConnections
|
||||||
|
@@ -1,5 +1,9 @@
|
|||||||
function Format-MissingAction {
|
function Format-MissingAction {
|
||||||
param ([array]$missingActions)
|
[CmdletBinding()]
|
||||||
|
[OutputType([hashtable])]
|
||||||
|
param (
|
||||||
|
[array]$missingActions
|
||||||
|
)
|
||||||
|
|
||||||
$actionGroups = @{
|
$actionGroups = @{
|
||||||
"Admin" = @()
|
"Admin" = @()
|
||||||
@@ -22,4 +26,4 @@ function Format-MissingAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $formattedResults
|
return $formattedResults
|
||||||
}
|
}
|
||||||
|
19
source/Private/Format-RequiredModuleList.ps1
Normal file
19
source/Private/Format-RequiredModuleList.ps1
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
function Format-RequiredModuleList {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[System.Object[]]$RequiredModules
|
||||||
|
)
|
||||||
|
|
||||||
|
$requiredModulesFormatted = ""
|
||||||
|
foreach ($module in $RequiredModules) {
|
||||||
|
if ($module.SubModules -and $module.SubModules.Count -gt 0) {
|
||||||
|
$subModulesFormatted = $module.SubModules -join ', '
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName) (SubModules: $subModulesFormatted), "
|
||||||
|
} else {
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName), "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $requiredModulesFormatted.TrimEnd(", ")
|
||||||
|
}
|
@@ -1,4 +1,6 @@
|
|||||||
function Get-MostCommonWord {
|
function Get-MostCommonWord {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string[]]$InputStrings
|
[string[]]$InputStrings
|
||||||
@@ -19,4 +21,4 @@ function Get-MostCommonWord {
|
|||||||
} else {
|
} else {
|
||||||
return $null
|
return $null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -12,22 +12,16 @@ function Get-RequiredModule {
|
|||||||
switch ($PSCmdlet.ParameterSetName) {
|
switch ($PSCmdlet.ParameterSetName) {
|
||||||
'AuditFunction' {
|
'AuditFunction' {
|
||||||
return @(
|
return @(
|
||||||
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" },
|
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
|
||||||
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" },
|
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() },
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" },
|
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" },
|
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Groups" },
|
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "DirectoryObjects" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Domains" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Reports" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Mail" },
|
|
||||||
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000" },
|
|
||||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0" }
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
'SyncFunction' {
|
'SyncFunction' {
|
||||||
return @(
|
return @(
|
||||||
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9" }
|
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9"; SubModules = @() }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
default {
|
default {
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
function Get-TestDefinitionsObject {
|
function Get-TestDefinitionsObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([object[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[object[]]$TestDefinitions,
|
[object[]]$TestDefinitions,
|
||||||
@@ -60,4 +62,4 @@ function Get-TestDefinitionsObject {
|
|||||||
|
|
||||||
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
||||||
return $TestDefinitions
|
return $TestDefinitions
|
||||||
}
|
}
|
||||||
|
28
source/Private/Get-UniqueConnection.ps1
Normal file
28
source/Private/Get-UniqueConnection.ps1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
function Get-UniqueConnection {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Connections
|
||||||
|
)
|
||||||
|
|
||||||
|
$uniqueConnections = @()
|
||||||
|
|
||||||
|
if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "AzureAD"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "Microsoft Graph"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "EXO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "SPO") {
|
||||||
|
$uniqueConnections += "SPO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Teams" -or $Connections -contains "Microsoft Teams | EXO") {
|
||||||
|
$uniqueConnections += "Microsoft Teams"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uniqueConnections | Sort-Object -Unique
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
function Initialize-CISAuditResult {
|
function Initialize-CISAuditResult {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Rec,
|
[string]$Rec,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Invoke-TestFunction {
|
function Invoke-TestFunction {
|
||||||
|
[OutputType([CISAuditResult[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[PSObject]$FunctionFile,
|
[PSObject]$FunctionFile,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Measure-AuditResult {
|
function Measure-AuditResult {
|
||||||
|
[OutputType([void])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[System.Collections.ArrayList]$AllAuditResults,
|
[System.Collections.ArrayList]$AllAuditResults,
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Merge-CISExcelAndCsvData {
|
function Merge-CISExcelAndCsvData {
|
||||||
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
|
[OutputType([PSCustomObject[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$ExcelPath,
|
[string]$ExcelPath,
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
function New-MergedObject {
|
function New-MergedObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([PSCustomObject])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[psobject]$ExcelItem,
|
[psobject]$ExcelItem,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
function Update-CISExcelWorksheet {
|
function Update-CISExcelWorksheet {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
|
@@ -1,28 +1,29 @@
|
|||||||
function Update-WorksheetCell {
|
function Update-WorksheetCell {
|
||||||
param (
|
[OutputType([void])]
|
||||||
$Worksheet,
|
param (
|
||||||
$Data,
|
$Worksheet,
|
||||||
$StartingRowIndex
|
$Data,
|
||||||
)
|
$StartingRowIndex
|
||||||
|
)
|
||||||
|
|
||||||
# Check and set headers
|
# Check and set headers
|
||||||
$firstItem = $Data[0]
|
$firstItem = $Data[0]
|
||||||
$colIndex = 1
|
$colIndex = 1
|
||||||
foreach ($property in $firstItem.PSObject.Properties) {
|
foreach ($property in $firstItem.PSObject.Properties) {
|
||||||
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
||||||
$Worksheet.Cells[1, $colIndex].Value = $property.Name
|
$Worksheet.Cells[1, $colIndex].Value = $property.Name
|
||||||
}
|
|
||||||
$colIndex++
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over each row in the data and update cells
|
|
||||||
$rowIndex = $StartingRowIndex
|
|
||||||
foreach ($item in $Data) {
|
|
||||||
$colIndex = 1
|
|
||||||
foreach ($property in $item.PSObject.Properties) {
|
|
||||||
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
|
|
||||||
$colIndex++
|
|
||||||
}
|
|
||||||
$rowIndex++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$colIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over each row in the data and update cells
|
||||||
|
$rowIndex = $StartingRowIndex
|
||||||
|
foreach ($item in $Data) {
|
||||||
|
$colIndex = 1
|
||||||
|
foreach ($property in $item.PSObject.Properties) {
|
||||||
|
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
|
||||||
|
$colIndex++
|
||||||
|
}
|
||||||
|
$rowIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -114,7 +114,6 @@
|
|||||||
.LINK
|
.LINK
|
||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||||
#>
|
#>
|
||||||
|
|
||||||
function Invoke-M365SecurityAudit {
|
function Invoke-M365SecurityAudit {
|
||||||
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
||||||
[OutputType([CISAuditResult[]])]
|
[OutputType([CISAuditResult[]])]
|
||||||
@@ -150,12 +149,12 @@ function Invoke-M365SecurityAudit {
|
|||||||
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
||||||
[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', `
|
||||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
||||||
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
||||||
'8.5.7', '8.6.1'
|
'8.5.7', '8.6.1'
|
||||||
)]
|
)]
|
||||||
[string[]]$IncludeRecommendation,
|
[string[]]$IncludeRecommendation,
|
||||||
|
|
||||||
@@ -163,12 +162,12 @@ function Invoke-M365SecurityAudit {
|
|||||||
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
||||||
[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', `
|
||||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
|
||||||
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
|
||||||
'8.5.7', '8.6.1'
|
'8.5.7', '8.6.1'
|
||||||
)]
|
)]
|
||||||
[string[]]$SkipRecommendation,
|
[string[]]$SkipRecommendation,
|
||||||
|
|
||||||
@@ -183,12 +182,18 @@ function Invoke-M365SecurityAudit {
|
|||||||
$script:MaximumFunctionCount = 8192
|
$script:MaximumFunctionCount = 8192
|
||||||
}
|
}
|
||||||
# Ensure required modules are installed
|
# Ensure required modules are installed
|
||||||
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules", "Check")) {
|
$requiredModules = Get-RequiredModule -AuditFunction
|
||||||
$requiredModules = Get-RequiredModule -AuditFunction
|
|
||||||
|
# Format the required modules list
|
||||||
|
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
|
||||||
|
|
||||||
|
# Check and install required modules if necessary
|
||||||
|
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules: $requiredModulesFormatted", "Check")) {
|
||||||
foreach ($module in $requiredModules) {
|
foreach ($module in $requiredModules) {
|
||||||
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
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
|
||||||
@@ -207,7 +212,7 @@ function Invoke-M365SecurityAudit {
|
|||||||
$testDefinitions = Get-TestDefinitionsObject @params
|
$testDefinitions = Get-TestDefinitionsObject @params
|
||||||
# Extract unique connections needed
|
# Extract unique connections needed
|
||||||
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
||||||
if ($requiredConnections -contains 'SPO'){
|
if ($requiredConnections -contains 'SPO') {
|
||||||
if (-not $TenantAdminUrl) {
|
if (-not $TenantAdminUrl) {
|
||||||
$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' }
|
||||||
@@ -235,10 +240,14 @@ function Invoke-M365SecurityAudit {
|
|||||||
$currentTestIndex = 0
|
$currentTestIndex = 0
|
||||||
|
|
||||||
# Establishing connections if required
|
# Establishing connections if required
|
||||||
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services", "Connect")) {
|
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
||||||
|
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
|
||||||
|
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" -InformationAction Continue
|
||||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Write-Information "A total of $($totalTests) tests were selected to run..." -InformationAction Continue
|
||||||
# Import the test functions
|
# Import the test functions
|
||||||
$testFiles | ForEach-Object {
|
$testFiles | ForEach-Object {
|
||||||
$currentTestIndex++
|
$currentTestIndex++
|
||||||
@@ -269,11 +278,11 @@ function Invoke-M365SecurityAudit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
End {
|
End {
|
||||||
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services", "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
|
||||||
}
|
}
|
||||||
if ($PSCmdlet.ShouldProcess("Measure and display audit results", "Measure")) {
|
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
|
||||||
# Call the private function to calculate and display results
|
# Call the private function to calculate and display results
|
||||||
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
||||||
# Return all collected audit results
|
# Return all collected audit results
|
||||||
@@ -281,3 +290,4 @@ function Invoke-M365SecurityAudit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -44,6 +44,7 @@ If the SkipUpdate switch is used, the function returns an array of custom object
|
|||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
||||||
#>
|
#>
|
||||||
function Sync-CISExcelAndCsvData {
|
function Sync-CISExcelAndCsvData {
|
||||||
|
[OutputType([void], [PSCustomObject[]])]
|
||||||
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
|
@@ -42,7 +42,7 @@ function Test-PasswordNeverExpirePolicy {
|
|||||||
$failureReasons = if ($isCompliant) {
|
$failureReasons = if ($isCompliant) {
|
||||||
"N/A"
|
"N/A"
|
||||||
} else {
|
} else {
|
||||||
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30"
|
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30`n"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = "$domainName|$passwordPolicy days|$isDefault"
|
$details = "$domainName|$passwordPolicy days|$isDefault"
|
||||||
|
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user