diff --git a/CHANGELOG.md b/CHANGELOG.md index 924569f..7ee733f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ The format is based on and uses the types of changes according to [Keep a Change ### 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. ### 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. - 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 ### Added diff --git a/helpers/Build-Help.ps1 b/helpers/Build-Help.ps1 index 3cda1d0..4b00ef4 100644 --- a/helpers/Build-Help.ps1 +++ b/helpers/Build-Help.ps1 @@ -4,7 +4,7 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1 <# - $ver = "v0.1.6" + $ver = "v0.1.7" git checkout main git pull origin main git tag -a $ver -m "Release version $ver refactor Update" diff --git a/source/Private/Assert-ModuleAvailability.ps1 b/source/Private/Assert-ModuleAvailability.ps1 index 81274fe..ea4be62 100644 --- a/source/Private/Assert-ModuleAvailability.ps1 +++ b/source/Private/Assert-ModuleAvailability.ps1 @@ -1,33 +1,37 @@ function Assert-ModuleAvailability { + [OutputType([void]) ] param( [string]$ModuleName, [string]$RequiredVersion, - [string]$SubModuleName + [string[]]$SubModules = @() ) try { $module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion } - if ($null -eq $module) {$auditResult.Profile - Write-Host "Installing $ModuleName module..." + if ($null -eq $module) { + Write-Information "Installing $ModuleName module..." -InformationAction Continue Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null } 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 } 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) { - Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null - } - else { + if ($SubModules.Count -gt 0) { + foreach ($subModule in $SubModules) { + Write-Information "Importing submodule $ModuleName.$subModule..." -InformationAction Continue + 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 } } catch { Write-Warning "An error occurred with module $ModuleName`: $_" } -} \ No newline at end of file +} diff --git a/source/Private/Connect-M365Suite.ps1 b/source/Private/Connect-M365Suite.ps1 index 3e2404e..1d57549 100644 --- a/source/Private/Connect-M365Suite.ps1 +++ b/source/Private/Connect-M365Suite.ps1 @@ -1,4 +1,5 @@ function Connect-M365Suite { + [OutputType([void])] [CmdletBinding()] param ( [Parameter(Mandatory=$false)] diff --git a/source/Private/Disconnect-M365Suite.ps1 b/source/Private/Disconnect-M365Suite.ps1 index 8d14b89..b338f67 100644 --- a/source/Private/Disconnect-M365Suite.ps1 +++ b/source/Private/Disconnect-M365Suite.ps1 @@ -1,4 +1,5 @@ function Disconnect-M365Suite { + [OutputType([void])] param ( [Parameter(Mandatory)] [string[]]$RequiredConnections diff --git a/source/Private/Format-MissingAction.ps1 b/source/Private/Format-MissingAction.ps1 index 1932fb1..5440486 100644 --- a/source/Private/Format-MissingAction.ps1 +++ b/source/Private/Format-MissingAction.ps1 @@ -1,5 +1,9 @@ function Format-MissingAction { - param ([array]$missingActions) + [CmdletBinding()] + [OutputType([hashtable])] + param ( + [array]$missingActions + ) $actionGroups = @{ "Admin" = @() @@ -22,4 +26,4 @@ function Format-MissingAction { } return $formattedResults -} \ No newline at end of file +} diff --git a/source/Private/Format-RequiredModuleList.ps1 b/source/Private/Format-RequiredModuleList.ps1 new file mode 100644 index 0000000..a3d56f6 --- /dev/null +++ b/source/Private/Format-RequiredModuleList.ps1 @@ -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(", ") +} diff --git a/source/Private/Get-MostCommonWord.ps1 b/source/Private/Get-MostCommonWord.ps1 index b626b5e..629a6cb 100644 --- a/source/Private/Get-MostCommonWord.ps1 +++ b/source/Private/Get-MostCommonWord.ps1 @@ -1,4 +1,6 @@ function Get-MostCommonWord { + [CmdletBinding()] + [OutputType([string])] param ( [Parameter(Mandatory = $true)] [string[]]$InputStrings @@ -19,4 +21,4 @@ function Get-MostCommonWord { } else { return $null } -} \ No newline at end of file +} diff --git a/source/Private/Get-RequiredModule.ps1 b/source/Private/Get-RequiredModule.ps1 index 015036b..e3b410a 100644 --- a/source/Private/Get-RequiredModule.ps1 +++ b/source/Private/Get-RequiredModule.ps1 @@ -12,22 +12,16 @@ function Get-RequiredModule { switch ($PSCmdlet.ParameterSetName) { 'AuditFunction' { return @( - @{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" }, - @{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" }, - @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" }, - @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" }, - @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Groups" }, - @{ 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" } + @{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() }, + @{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() }, + @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") }, + @{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() }, + @{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() } ) } 'SyncFunction' { return @( - @{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9" } + @{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9"; SubModules = @() } ) } default { diff --git a/source/Private/Get-TestDefinitionsObject.ps1 b/source/Private/Get-TestDefinitionsObject.ps1 index 6e42d23..da94a14 100644 --- a/source/Private/Get-TestDefinitionsObject.ps1 +++ b/source/Private/Get-TestDefinitionsObject.ps1 @@ -1,4 +1,6 @@ function Get-TestDefinitionsObject { + [CmdletBinding()] + [OutputType([object[]])] param ( [Parameter(Mandatory = $true)] [object[]]$TestDefinitions, @@ -60,4 +62,4 @@ function Get-TestDefinitionsObject { Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)" return $TestDefinitions -} \ No newline at end of file +} diff --git a/source/Private/Get-UniqueConnection.ps1 b/source/Private/Get-UniqueConnection.ps1 new file mode 100644 index 0000000..a05f282 --- /dev/null +++ b/source/Private/Get-UniqueConnection.ps1 @@ -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 +} diff --git a/source/Private/Initialize-CISAuditResult.ps1 b/source/Private/Initialize-CISAuditResult.ps1 index 3b2cc94..66adf73 100644 --- a/source/Private/Initialize-CISAuditResult.ps1 +++ b/source/Private/Initialize-CISAuditResult.ps1 @@ -1,5 +1,6 @@ function Initialize-CISAuditResult { [CmdletBinding()] + [OutputType([CISAuditResult])] param ( [Parameter(Mandatory = $true)] [string]$Rec, diff --git a/source/Private/Invoke-TestFunction.ps1 b/source/Private/Invoke-TestFunction.ps1 index 5f6b944..ac5ca2c 100644 --- a/source/Private/Invoke-TestFunction.ps1 +++ b/source/Private/Invoke-TestFunction.ps1 @@ -1,4 +1,5 @@ function Invoke-TestFunction { + [OutputType([CISAuditResult[]])] param ( [Parameter(Mandatory = $true)] [PSObject]$FunctionFile, diff --git a/source/Private/Measure-AuditResult.ps1 b/source/Private/Measure-AuditResult.ps1 index d03265e..041fa25 100644 --- a/source/Private/Measure-AuditResult.ps1 +++ b/source/Private/Measure-AuditResult.ps1 @@ -1,4 +1,5 @@ function Measure-AuditResult { + [OutputType([void])] param ( [Parameter(Mandatory = $true)] [System.Collections.ArrayList]$AllAuditResults, diff --git a/source/Private/Merge-CISExcelAndCsvData.ps1 b/source/Private/Merge-CISExcelAndCsvData.ps1 index 0922c9b..7d2f039 100644 --- a/source/Private/Merge-CISExcelAndCsvData.ps1 +++ b/source/Private/Merge-CISExcelAndCsvData.ps1 @@ -1,5 +1,6 @@ function Merge-CISExcelAndCsvData { [CmdletBinding(DefaultParameterSetName = 'CsvInput')] + [OutputType([PSCustomObject[]])] param ( [Parameter(Mandatory = $true)] [string]$ExcelPath, diff --git a/source/Private/New-MergedObject.ps1 b/source/Private/New-MergedObject.ps1 index 4840d73..50f7497 100644 --- a/source/Private/New-MergedObject.ps1 +++ b/source/Private/New-MergedObject.ps1 @@ -1,4 +1,6 @@ function New-MergedObject { + [CmdletBinding()] + [OutputType([PSCustomObject])] param ( [Parameter(Mandatory = $true)] [psobject]$ExcelItem, diff --git a/source/Private/Update-CISExcelWorksheet.ps1 b/source/Private/Update-CISExcelWorksheet.ps1 index a0d7ae1..9976f76 100644 --- a/source/Private/Update-CISExcelWorksheet.ps1 +++ b/source/Private/Update-CISExcelWorksheet.ps1 @@ -1,4 +1,5 @@ function Update-CISExcelWorksheet { + [OutputType([void])] [CmdletBinding()] param ( [Parameter(Mandatory = $true)] diff --git a/source/Private/Update-WorksheetCell.ps1 b/source/Private/Update-WorksheetCell.ps1 index 622f963..9708c1c 100644 --- a/source/Private/Update-WorksheetCell.ps1 +++ b/source/Private/Update-WorksheetCell.ps1 @@ -1,28 +1,29 @@ - function Update-WorksheetCell { - param ( - $Worksheet, - $Data, - $StartingRowIndex - ) +function Update-WorksheetCell { + [OutputType([void])] + param ( + $Worksheet, + $Data, + $StartingRowIndex + ) - # Check and set headers - $firstItem = $Data[0] - $colIndex = 1 - foreach ($property in $firstItem.PSObject.Properties) { - if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) { - $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++ - } + # Check and set headers + $firstItem = $Data[0] + $colIndex = 1 + foreach ($property in $firstItem.PSObject.Properties) { + if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) { + $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++ + } +} diff --git a/source/Public/Invoke-M365SecurityAudit.ps1 b/source/Public/Invoke-M365SecurityAudit.ps1 index abdaa72..5e94add 100644 --- a/source/Public/Invoke-M365SecurityAudit.ps1 +++ b/source/Public/Invoke-M365SecurityAudit.ps1 @@ -114,7 +114,6 @@ .LINK https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit #> - function Invoke-M365SecurityAudit { [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')] [OutputType([CISAuditResult[]])] @@ -150,12 +149,12 @@ function Invoke-M365SecurityAudit { [Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')] [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', ` - '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', ` - '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', ` - '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' + '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', ` + '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', ` + '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' )] [string[]]$IncludeRecommendation, @@ -163,12 +162,12 @@ function Invoke-M365SecurityAudit { [Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')] [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', ` - '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', ` - '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', ` - '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' + '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', ` + '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', ` + '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' )] [string[]]$SkipRecommendation, @@ -183,12 +182,18 @@ function Invoke-M365SecurityAudit { $script:MaximumFunctionCount = 8192 } # 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) { - 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 $testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv" $testDefinitions = Import-Csv -Path $testDefinitionsPath @@ -207,7 +212,7 @@ function Invoke-M365SecurityAudit { $testDefinitions = Get-TestDefinitionsObject @params # Extract unique connections needed $requiredConnections = $testDefinitions.Connection | Sort-Object -Unique - if ($requiredConnections -contains 'SPO'){ + if ($requiredConnections -contains 'SPO') { if (-not $TenantAdminUrl) { $requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' } $testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' } @@ -235,10 +240,14 @@ function Invoke-M365SecurityAudit { $currentTestIndex = 0 # 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 } + + Write-Information "A total of $($totalTests) tests were selected to run..." -InformationAction Continue # Import the test functions $testFiles | ForEach-Object { $currentTestIndex++ @@ -269,11 +278,11 @@ function Invoke-M365SecurityAudit { } 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 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 Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests # Return all collected audit results @@ -281,3 +290,4 @@ function Invoke-M365SecurityAudit { } } } + diff --git a/source/Public/Sync-CISExcelAndCsvData.ps1 b/source/Public/Sync-CISExcelAndCsvData.ps1 index 8842e2f..8e91b95 100644 --- a/source/Public/Sync-CISExcelAndCsvData.ps1 +++ b/source/Public/Sync-CISExcelAndCsvData.ps1 @@ -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 #> function Sync-CISExcelAndCsvData { + [OutputType([void], [PSCustomObject[]])] [CmdletBinding(DefaultParameterSetName = 'CsvInput')] param ( [Parameter(Mandatory = $true)] diff --git a/source/tests/Test-PasswordNeverExpirePolicy.ps1 b/source/tests/Test-PasswordNeverExpirePolicy.ps1 index 7cf400a..b94a22e 100644 --- a/source/tests/Test-PasswordNeverExpirePolicy.ps1 +++ b/source/tests/Test-PasswordNeverExpirePolicy.ps1 @@ -42,7 +42,7 @@ function Test-PasswordNeverExpirePolicy { $failureReasons = if ($isCompliant) { "N/A" } 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" diff --git a/tests/Unit/Private/Format-RequiredModuleList.tests.ps1 b/tests/Unit/Private/Format-RequiredModuleList.tests.ps1 new file mode 100644 index 0000000..4a2aa69 --- /dev/null +++ b/tests/Unit/Private/Format-RequiredModuleList.tests.ps1 @@ -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' + } + } + } +} + diff --git a/tests/Unit/Private/Get-UniqueConnection.tests.ps1 b/tests/Unit/Private/Get-UniqueConnection.tests.ps1 new file mode 100644 index 0000000..4a2aa69 --- /dev/null +++ b/tests/Unit/Private/Get-UniqueConnection.tests.ps1 @@ -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' + } + } + } +} +