63 Commits

Author SHA1 Message Date
Doug Rios
0b3213d957 Merge pull request #17 from CriticalSolutionsNetwork/Fix-domain-test
Fix domain test
2024-06-08 15:58:29 -05:00
DrIOS
9d9b9e70d9 docs: update help 2024-06-08 15:56:25 -05:00
DrIOS
4167a37121 docs: Update CHANGELOG.md 2024-06-08 15:54:48 -05:00
DrIOS
66536e34a7 fix: update domain pw policy logic 2024-06-08 15:53:12 -05:00
DrIOS
db9b206ae3 docs: Update help 2024-06-08 15:00:10 -05:00
DrIOS
8a9044486b docs: Update Help and README 2024-06-08 14:56:51 -05:00
Doug Rios
447be9cacb Merge pull request #12 from CriticalSolutionsNetwork/Better-Error-Handling
Better error handling
2024-06-08 14:52:15 -05:00
Doug Rios
71c798c52a Merge pull request #16 from CriticalSolutionsNetwork/main
Create SECURITY.md
2024-06-08 14:40:38 -05:00
Doug Rios
d0270027f9 Create SECURITY.md 2024-06-08 14:39:13 -05:00
DrIOS
0569fd98cc test: update workflow 2024-06-08 14:36:41 -05:00
DrIOS
c842ae9720 test: update workflow 2024-06-08 14:32:43 -05:00
DrIOS
63b9e8b75d test: update workflow 2024-06-08 14:28:19 -05:00
DrIOS
f7b87ebc78 test: update workflow 2024-06-08 14:25:00 -05:00
DrIOS
b70da1845f addL paramter validation to main function 2024-06-08 14:10:55 -05:00
DrIOS
ef55447e67 docs:update help 2024-06-08 14:03:55 -05:00
DrIOS
7a9d2885f3 fix: script analyzer reccomendations. 2024-06-08 13:29:48 -05:00
DrIOS
4ab5affc9f fix: script analyzer reccomendations. 2024-06-08 13:26:13 -05:00
DrIOS
5871294210 add: progress bar and surpressed verbose progress. 2024-06-08 13:01:39 -05:00
DrIOS
a5dc7f1ebd docs: update help 2024-06-08 12:38:47 -05:00
DrIOS
c0222ef3bc add:comment-help block back to public function 2024-06-08 12:11:01 -05:00
DrIOS
83ee6c2ac3 add: New private function for csv/xlsx merge 2024-06-08 12:02:16 -05:00
DrIOS
cbdb31c7c5 add: better output to 1.1.3 2024-06-08 11:54:54 -05:00
DrIOS
a5d26917d3 add: paramters to get-requredmodules and updated public functions 2024-06-08 11:51:54 -05:00
DrIOS
4f8df29c72 add: Output type to tests. 2024-06-08 11:35:25 -05:00
DrIOS
bb1df11128 add: Output type to tests. 2024-06-08 11:34:21 -05:00
DrIOS
2d9a1a1d10 docs: Update CHANGELOG 2024-06-08 11:32:10 -05:00
DrIOS
41dbf2f0db add: Output type to tests. 2024-06-08 11:31:40 -05:00
DrIOS
70dcd74643 docs: Update CHANGELOG 2024-06-08 11:28:13 -05:00
DrIOS
f6aa4b83dd add: parameter validation for excel and csv path in sync function 2024-06-08 11:27:34 -05:00
DrIOS
d58d0b664d docs: Update CHANGELOG 2024-06-08 11:25:47 -05:00
DrIOS
b9a8a75945 fix: minimized mggraph call count and adjusted output in 1.1.1 2024-06-08 11:20:02 -05:00
DrIOS
6495073a10 fix: step 1 and step 2 in 6.2.1 details. 2024-06-08 10:39:29 -05:00
DrIOS
e0e2a04b6a fix: add check for length in 7.3.4 details. 2024-06-08 09:13:46 -05:00
DrIOS
d85968935b fix: remove verbose preference from 7.3.4 2024-06-07 21:37:28 -05:00
DrIOS
f47efa74f9 docs: Update changelog 2024-06-07 21:20:24 -05:00
DrIOS
f905f269d1 fix: output for 6.1.2/3 and 7.3.4 2024-06-07 21:10:28 -05:00
DrIOS
8719900af7 test: testingoutput 2024-06-07 20:49:37 -05:00
DrIOS
8922ea12cd fix: improve output of 7.3.4 2024-06-07 18:08:15 -05:00
DrIOS
c6bdad0477 fix: improve output of 6.1.2 2024-06-07 18:07:36 -05:00
DrIOS
85bd0fb19f add: sort to output 2024-06-07 18:06:47 -05:00
DrIOS
8de61dda9f fix: 7.3.4 so it outputs a pipe separated table 2024-06-07 14:15:57 -05:00
DrIOS
c530d2df76 docs: Update CHANGELOG 2024-06-07 13:39:12 -05:00
DrIOS
f5ab68dd63 fix: output in 6.2.1 so joine won't fail 2024-06-07 13:04:33 -05:00
DrIOS
d9ed3b60b7 fix: output in 6.1.2,6.1.3 update changelog 2024-06-07 10:31:38 -05:00
DrIOS
4857aead5e docs: update changelog 2024-06-06 16:56:58 -05:00
DrIOS
92e5952f7e Add: Module function 2024-06-06 16:40:18 -05:00
DrIOS
cb52ce1a73 docs: update CHANGELOG 2024-06-06 14:43:02 -05:00
DrIOS
4bc1f8fdda change: refactored test output and created new functions 2024-06-06 14:38:19 -05:00
DrIOS
8446d101a7 fix: connections in helper csv and connect function 2024-06-05 17:40:45 -05:00
DrIOS
06aeadaee5 fix: connections in helper csv and connect function 2024-06-05 15:43:45 -05:00
DrIOS
a9e44a7c6f fix: Removed nested functions in 6.1.2/6.1.3 2024-06-05 15:07:04 -05:00
DrIOS
2712f78412 docs: update CHANGELOG 2024-06-05 15:05:21 -05:00
DrIOS
df89e23bc1 add: simplified object build call 2024-06-05 14:07:19 -05:00
DrIOS
2027e8b21b Add: error handling to tests 2024-06-04 17:04:18 -05:00
Doug Rios
5c60f39dad Merge pull request #11 from CriticalSolutionsNetwork/Add-additional-properties-to-merged-doc
Add additional properties to merged doc / refactor connections and filtering of tests.
2024-05-30 16:55:56 -05:00
DrIOS
399288b10a add: add score to console host 2024-05-30 16:41:38 -05:00
DrIOS
87d0aaaea6 docs: Update CHANGELOG 2024-05-30 16:21:23 -05:00
DrIOS
8301b8cdbe add: Logging functions for future use 2024-05-30 16:19:05 -05:00
DrIOS
3f4166e98a add: refactor for connect/disconnect 2024-05-30 16:18:47 -05:00
DrIOS
7582511dd5 add: filter function for tests 2024-05-30 15:03:59 -05:00
DrIOS
20afb8d83e add: connection property to merge 2024-05-30 12:10:14 -05:00
Doug Rios
c378f5d119 Create powershell.yml 2024-05-28 21:19:28 -05:00
DrIOS
3a37d465e8 docs: Update Readme and Changelog 2024-05-28 20:46:23 -05:00
87 changed files with 3161 additions and 1447 deletions

50
.github/workflows/powershell.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
#
# https://github.com/microsoft/action-psscriptanalyzer
# For more information on PSScriptAnalyzer in general, see
# https://github.com/PowerShell/PSScriptAnalyzer
name: PSScriptAnalyzer
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
schedule:
- cron: '33 20 * * 4'
permissions:
contents: read
jobs:
build:
permissions:
contents: read # for actions/checkout to fetch code
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
name: PSScriptAnalyzer
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run PSScriptAnalyzer
uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f
with:
# Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options.
# The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules.
path: .\source
recurse: true
# Include your own basic security rules. Removing this option will run all the rules
includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText", "PSAvoidUsingPlainTextForPassword", "PSAvoidUsingInvokeExpression", "PSUseApprovedVerbs", "PSAvoidUsingPositionalParameters", "PSAvoidUsingEmptyCatchBlock", "PSAvoidUsingDeprecatedManifestFields", "PSAvoidUsingUserNameAndPasswordParams", "PSAvoidUsingCmdletAliases"'
output: results.sarif
# Upload the SARIF file generated in the previous step
- name: Upload SARIF results file
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: results.sarif

View File

@@ -6,6 +6,53 @@ The format is based on and uses the types of changes according to [Keep a Change
### Added
- Updated test definitions for CIS Microsoft 365 Foundations Benchmark for better error handling and object output when errors occur.
- Added a parameter to the `Initialize-CISAuditResult` function to allow for a static failed object to be created when an error occurs.
- Refactored `Invoke-M365SecurityAudit` to include a new private function `Invoke-TestFunction` for executing test functions and handling errors.
- Added a new private function `Measure-AuditResult` to calculate and display audit results.
- Enhanced error logging to capture failed test details and display them at the end of the audit.
- Added a private function `Get-RequiredModule` to initialize the `$requiredModules` variable for better code organization in the main script.
- Updated `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to use `Format-MissingAction` for structuring missing actions into a pipe-separated table format.
- Added more verbose logging to `Test-BlockMailForwarding` and improved error handling for better troubleshooting.
- Improved `Test-RestrictCustomScripts` to handle long URL lengths better by extracting and replacing common hostnames, and provided detailed output.
- Added sorting to output.
- Created new functions for improved modularity.
- Parameter validation for Excel and CSV path in sync function.
- Added Output type to tests.
- Added `M365DomainForPWPolicyTest` parameter to `Invoke-M365SecurityAudit` to specify testing only the default domain for password expiration policy when '1.3.1' is included in the tests.
### Fixed
- Ensured the `Invoke-TestFunction` returns a `CISAuditResult` object, which is then managed in the `Invoke-M365SecurityAudit` function.
- Corrected the usage of the join operation within `$details` in `Test-BlockMailForwarding` to handle arrays properly.
- Fixed the logic in `Test-RestrictCustomScripts` to accurately replace and manage URLs, ensuring compliance checks are correctly performed.
- Updated the `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to handle the `$allFailures` variable correctly, ensuring accurate pass/fail results.
- Fixed the connections in helper CSV and connect function.
- Removed verbose preference from `Test-RestrictCustomScripts`.
- Ensured that the output in `Test-BlockMailForwarding` does not include extra spaces between table headers and data.
- Fixed output in `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` to correctly align with the new table format.
- Added step 1 and step 2 in `Test-BlockMailForwarding` details to ensure comprehensive compliance checks.
- Fixed the issue with the output in `Test-RestrictCustomScripts` to ensure no extra spaces between table headers and data.
## [0.1.4] - 2024-05-30
### Added
- Test definitions filter function.
- Logging function for future use.
- Test grade written to console.
### Changed
- Updated sync function to include connection info.
- Refactored connect/disconnect functions to evaluate needed connections.
## [0.1.3] - 2024-05-28
### Added
- Array list to store the results of the audit.
- Arraylist tests and helper template.
- New testing function.
@@ -27,7 +74,6 @@ The format is based on and uses the types of changes according to [Keep a Change
- Updated comments and documentation for new functions.
## [0.1.2] - 2024-04-29
### Added

BIN
README.md

Binary file not shown.

18
SECURITY.md Normal file
View File

@@ -0,0 +1,18 @@
# Security Policy
## Supported Versions
Use this section to tell people about which versions of your project are
currently being supported with security updates.
| Version | Supported |
| ------- | ------------------ |
| 0.1.4 | :white_check_mark: |
## Reporting a Vulnerability
Use this section to tell people how to report a vulnerability.
Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.

Binary file not shown.

View File

@@ -4,10 +4,10 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
<#
$ver = "v0.1.2"
$ver = "v0.1.5"
git checkout main
git pull origin main
git tag -a $ver -m "Release version $ver Bugfix Update"
git tag -a $ver -m "Release version $ver refactor Update"
git push origin $ver
"Fix: PR #37"
git push origin $ver

View File

@@ -60,7 +60,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
}
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$formattedActions = Format-MissingAction $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
# Mark the user as processed
@@ -83,7 +83,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
}
}
function Format-MissingActions {
function Format-MissingAction {
param ([array]$missingActions)
$actionGroups = @{

View File

@@ -60,7 +60,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
}
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$formattedActions = Format-MissingAction $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
else {
@@ -92,7 +92,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
}
}
function Format-MissingActions {
function Format-MissingAction {
param ([array]$missingActions)
$actionGroups = @{

View File

@@ -1,56 +1,58 @@
function Connect-M365Suite {
[CmdletBinding()]
param (
# Parameter to specify the SharePoint Online Tenant Admin URL
[Parameter(Mandatory)]
[string]$TenantAdminUrl
)
$VerbosePreference = "SilentlyContinue"
try {
[string]$TenantAdminUrl,
# Attempt to connect to Azure Active Directory
[Parameter(Mandatory)]
[string[]]$RequiredConnections
)
$VerbosePreference = "SilentlyContinue"
try {
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
Connect-AzureAD | Out-Null
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
}
# Attempt to connect to Exchange Online
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
Connect-ExchangeOnline | Out-Null
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
try {
# Attempt to connect to Microsoft Graph with specified scopes
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
try {
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
}
catch {
Write-Host "Failed to connect o MgGraph, attempting device auth." -ForegroundColor Yellow
# Attempt to connect to Microsoft Graph with specified scopes
Write-Host "Connecting to Microsoft Graph using device auth with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
Write-Host "Failed to connect to MgGraph, attempting device auth." -ForegroundColor Yellow
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
}
# Validate SharePoint Online Tenant Admin URL
if (-not $TenantAdminUrl) {
throw "SharePoint Online Tenant Admin URL is required."
}
# Attempt to connect to SharePoint Online
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
Connect-ExchangeOnline | Out-Null
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
}
if ($RequiredConnections -contains "SPO") {
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
Connect-SPOService -Url $TenantAdminUrl | Out-Null
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
}
# Attempt to connect to Microsoft Teams
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
Connect-MicrosoftTeams | Out-Null
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
}
}
catch {
$VerbosePreference = "Continue"
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
throw $_
}
$VerbosePreference = "Continue"
}

View File

@@ -1,39 +1,59 @@
function Disconnect-M365Suite {
param (
[Parameter(Mandatory)]
[string[]]$RequiredConnections
)
# Clean up sessions
try {
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
}
}
catch {
Write-Warning "Failed to disconnect from Exchange Online: $_"
}
try {
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
Disconnect-AzureAD | Out-Null
}
}
catch {
Write-Warning "Failed to disconnect from Azure AD: $_"
}
try {
if ($RequiredConnections -contains "Microsoft Graph") {
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
Disconnect-MgGraph | Out-Null
}
}
catch {
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
}
try {
if ($RequiredConnections -contains "SPO") {
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
Disconnect-SPOService | Out-Null
}
}
catch {
Write-Warning "Failed to disconnect from SharePoint Online: $_"
}
try {
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
Disconnect-MicrosoftTeams | Out-Null
}
}
catch {
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
}
Write-Host "All sessions have been disconnected." -ForegroundColor Green
Write-Host "All necessary sessions have been disconnected." -ForegroundColor Green
}

View File

@@ -0,0 +1,25 @@
function Format-MissingAction {
param ([array]$missingActions)
$actionGroups = @{
"Admin" = @()
"Delegate" = @()
"Owner" = @()
}
foreach ($action in $missingActions) {
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
$type = $matches[1]
$actionName = $matches[2]
$actionGroups[$type] += $actionName
}
}
$formattedResults = @{
Admin = $actionGroups["Admin"] -join ', '
Delegate = $actionGroups["Delegate"] -join ', '
Owner = $actionGroups["Owner"] -join ', '
}
return $formattedResults
}

View File

@@ -0,0 +1,22 @@
function Get-MostCommonWord {
param (
[Parameter(Mandatory = $true)]
[string[]]$InputStrings
)
# Combine all strings into one large string
$allText = $InputStrings -join ' '
# Split the large string into words
$words = $allText -split '\s+'
# Group words and count occurrences
$wordGroups = $words | Group-Object | Sort-Object Count -Descending
# Return the most common word if it occurs at least 3 times
if ($wordGroups.Count -gt 0 -and $wordGroups[0].Count -ge 3) {
return $wordGroups[0].Name
} else {
return $null
}
}

View File

@@ -0,0 +1,37 @@
function Get-RequiredModule {
[CmdletBinding(DefaultParameterSetName = 'AuditFunction')]
[OutputType([System.Object[]])]
param (
[Parameter(Mandatory = $true, ParameterSetName = 'AuditFunction')]
[switch]$AuditFunction,
[Parameter(Mandatory = $true, ParameterSetName = 'SyncFunction')]
[switch]$SyncFunction
)
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" }
)
}
'SyncFunction' {
return @(
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9" }
)
}
default {
throw "Please specify either -AuditFunction or -SyncFunction switch."
}
}
}

View File

@@ -0,0 +1,63 @@
function Get-TestDefinitionsObject {
param (
[Parameter(Mandatory = $true)]
[object[]]$TestDefinitions,
[Parameter(Mandatory = $true)]
[string]$ParameterSetName,
[string]$ELevel,
[string]$ProfileLevel,
[string[]]$IncludeRecommendation,
[string[]]$SkipRecommendation
)
Write-Verbose "Initial test definitions count: $($TestDefinitions.Count)"
switch ($ParameterSetName) {
'ELevelFilter' {
Write-Verbose "Applying ELevelFilter"
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
Write-Verbose "Filtering on ELevel = $ELevel and ProfileLevel = $ProfileLevel"
$TestDefinitions = $TestDefinitions | Where-Object {
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
}
}
elseif ($null -ne $ELevel) {
Write-Verbose "Filtering on ELevel = $ELevel"
$TestDefinitions = $TestDefinitions | Where-Object {
$_.ELevel -eq $ELevel
}
}
elseif ($null -ne $ProfileLevel) {
Write-Verbose "Filtering on ProfileLevel = $ProfileLevel"
$TestDefinitions = $TestDefinitions | Where-Object {
$_.ProfileLevel -eq $ProfileLevel
}
}
}
'IG1Filter' {
Write-Verbose "Applying IG1Filter"
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
}
'IG2Filter' {
Write-Verbose "Applying IG2Filter"
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
}
'IG3Filter' {
Write-Verbose "Applying IG3Filter"
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
}
'RecFilter' {
Write-Verbose "Applying RecFilter"
$TestDefinitions = $TestDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
}
'SkipRecFilter' {
Write-Verbose "Applying SkipRecFilter"
$TestDefinitions = $TestDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
}
}
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
return $TestDefinitions
}

View File

@@ -1,19 +1,23 @@
function Initialize-CISAuditResult {
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)]
[string]$Rec,
[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[bool]$Result,
[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[string]$Status,
[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[string]$Details,
[Parameter(Mandatory = $true)]
[string]$FailureReason
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
[string]$FailureReason,
[Parameter(ParameterSetName = 'Error')]
[switch]$Failure
)
# Import the test definitions CSV file
@@ -22,6 +26,10 @@ function Initialize-CISAuditResult {
# Find the row that matches the provided recommendation (Rec)
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $Rec }
if (-not $testDefinition) {
throw "Test definition for recommendation '$Rec' not found."
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Rec = $Rec
@@ -36,10 +44,18 @@ function Initialize-CISAuditResult {
$auditResult.Automated = [bool]::Parse($testDefinition.Automated)
$auditResult.Connection = $testDefinition.Connection
$auditResult.CISControlVer = 'v8'
if ($PSCmdlet.ParameterSetName -eq 'Full') {
$auditResult.Result = $Result
$auditResult.Status = $Status
$auditResult.Details = $Details
$auditResult.FailureReason = $FailureReason
} elseif ($PSCmdlet.ParameterSetName -eq 'Error') {
$auditResult.Result = $false
$auditResult.Status = 'Fail'
$auditResult.Details = "An error occurred while processing the test."
$auditResult.FailureReason = "Initialization error: Failed to process the test."
}
return $auditResult
}

View File

@@ -0,0 +1,34 @@
function Invoke-TestFunction {
param (
[Parameter(Mandatory = $true)]
[PSObject]$FunctionFile,
[Parameter(Mandatory = $false)]
[string]$DomainName
)
$functionName = $FunctionFile.BaseName
$functionCmd = Get-Command -Name $functionName
# Check if the test function needs DomainName parameter
$paramList = @{}
if ('DomainName' -in $functionCmd.Parameters.Keys) {
$paramList.DomainName = $DomainName
}
# Use splatting to pass parameters
Write-Verbose "Running $functionName..."
try {
$result = & $functionName @paramList
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
return $result
}
catch {
Write-Error "An error occurred during the test: $_"
$script:FailedTests.Add([PSCustomObject]@{ Test = $functionName; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $functionName -Failure
return $auditResult
}
}

View File

@@ -0,0 +1,31 @@
function Measure-AuditResult {
param (
[Parameter(Mandatory = $true)]
[System.Collections.ArrayList]$AllAuditResults,
[Parameter(Mandatory = $false)]
[System.Collections.ArrayList]$FailedTests
)
# Calculate the total number of tests
$totalTests = $AllAuditResults.Count
# Calculate the number of passed tests
$passedTests = $AllAuditResults.ToArray() | Where-Object { $_.Result -eq $true } | Measure-Object | Select-Object -ExpandProperty Count
# Calculate the pass percentage
$passPercentage = if ($totalTests -eq 0) { 0 } else { [math]::Round(($passedTests / $totalTests) * 100, 2) }
# Display the pass percentage to the user
Write-Host "Audit completed. $passedTests out of $totalTests tests passed." -ForegroundColor Cyan
Write-Host "Your passing percentage is $passPercentage%."
# Display details of failed tests
if ($FailedTests.Count -gt 0) {
Write-Host "The following tests failed to complete:" -ForegroundColor Red
foreach ($failedTest in $FailedTests) {
Write-Host "Test: $($failedTest.Test)" -ForegroundColor Yellow
Write-Host "Error: $($failedTest.Error)" -ForegroundColor Yellow
}
}
}

View File

@@ -1,5 +1,5 @@
function Merge-CISExcelAndCsvData {
[CmdletBinding()]
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
param (
[Parameter(Mandatory = $true)]
[string]$ExcelPath,
@@ -7,37 +7,31 @@ function Merge-CISExcelAndCsvData {
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true)]
[string]$CsvPath
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
[string]$CsvPath,
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput')]
[CISAuditResult[]]$AuditResults
)
process {
# Import data from Excel and CSV
# Import data from Excel
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
$csvData = Import-Csv -Path $CsvPath
# Define a function to create a merged object
function CreateMergedObject($excelItem, $csvRow) {
$newObject = New-Object PSObject
foreach ($property in $excelItem.PSObject.Properties) {
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
# Import data from CSV or use provided object
$csvData = if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
Import-Csv -Path $CsvPath
} else {
$AuditResults
}
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $csvRow.Status
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $csvRow.Details
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $csvRow.FailureReason
return $newObject
}
# Iterate over each item in the imported Excel object and merge with CSV data
# Iterate over each item in the imported Excel object and merge with CSV data or audit results
$mergedData = foreach ($item in $import) {
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
if ($csvRow) {
CreateMergedObject -excelItem $item -csvRow $csvRow
New-MergedObject -ExcelItem $item -CsvRow $csvRow
} else {
CreateMergedObject -excelItem $item -csvRow ([PSCustomObject]@{Status=$null; Details=$null; FailureReason=$null})
New-MergedObject -ExcelItem $item -CsvRow ([PSCustomObject]@{Connection=$null;Status=$null; Details=$null; FailureReason=$null })
}
}

View File

@@ -0,0 +1,20 @@
function New-MergedObject {
param (
[Parameter(Mandatory = $true)]
[psobject]$ExcelItem,
[Parameter(Mandatory = $true)]
[psobject]$CsvRow
)
$newObject = New-Object PSObject
foreach ($property in $ExcelItem.PSObject.Properties) {
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
}
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Connection' -Value $CsvRow.Connection
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $CsvRow.Status
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $CsvRow.Details
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $CsvRow.FailureReason
return $newObject
}

View File

@@ -0,0 +1,22 @@
function Test-IsAdmin {
<#
.SYNOPSIS
Checks if the current user is an administrator on the machine.
.DESCRIPTION
This private function returns a Boolean value indicating whether
the current user has administrator privileges on the machine.
It does this by creating a new WindowsPrincipal object, passing
in a WindowsIdentity object representing the current user, and
then checking if that principal is in the Administrator role.
.INPUTS
None.
.OUTPUTS
Boolean. Returns True if the current user is an administrator, and False otherwise.
.EXAMPLE
PS C:\> Test-IsAdmin
True
#>
# Create a new WindowsPrincipal object for the current user and check if it is in the Administrator role
(New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
}

View File

@@ -25,7 +25,7 @@ function Update-CISExcelWorksheet {
# Update the worksheet with the provided data
Update-WorksheetCells -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
Update-WorksheetCell -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
# Save and close the Excel package
Close-ExcelPackage $excelPackage

View File

@@ -1,4 +1,4 @@
function Update-WorksheetCells {
function Update-WorksheetCell {
param (
$Worksheet,
$Data,

View File

@@ -0,0 +1,212 @@
function Write-AuditLog {
<#
.SYNOPSIS
Writes log messages to the console and updates the script-wide log variable.
.DESCRIPTION
The Write-AuditLog function writes log messages to the console based on the severity (Verbose, Warning, or Error) and updates
the script-wide log variable ($script:LogString) with the log entry. You can use the Start, End, and EndFunction switches to
manage the lifecycle of the logging.
.INPUTS
System.String
You can pipe a string to the Write-AuditLog function as the Message parameter.
You can also pipe an object with a Severity property as the Severity parameter.
.OUTPUTS
None
The Write-AuditLog function doesn't output any objects to the pipeline. It writes messages to the console and updates the
script-wide log variable ($script:LogString).
.PARAMETER BeginFunction
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.PARAMETER Message
The message string to log.
.PARAMETER Severity
The severity of the log message. Accepted values are 'Information', 'Warning', and 'Error'. Defaults to 'Information'.
.PARAMETER Start
Initializes the script-wide log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
.PARAMETER End
Sets the message to "End Log" and exports the log to a CSV file if the OutputPath parameter is provided.
.PARAMETER EndFunction
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.PARAMETER OutputPath
The file path for exporting the log to a CSV file when using the End switch.
.EXAMPLE
Write-AuditLog -Message "This is a test message."
Writes a test message with the default severity (Information) to the console and adds it to the log variable.
.EXAMPLE
Write-AuditLog -Message "This is a warning message." -Severity "Warning"
Writes a warning message to the console and adds it to the log variable.
.EXAMPLE
Write-AuditLog -Start
Initializes the log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
.EXAMPLE
Write-AuditLog -BeginFunction
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.EXAMPLE
Write-AuditLog -EndFunction
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
.EXAMPLE
Write-AuditLog -End -OutputPath "C:\Logs\auditlog.csv"
Sets the message to "End Log", adds it to the log variable, and exports the log to a CSV file.
.NOTES
Author: DrIOSx
#>
[CmdletBinding(DefaultParameterSetName = 'Default')]
param(
###
[Parameter(
Mandatory = $false,
HelpMessage = 'Input a Message string.',
Position = 0,
ParameterSetName = 'Default',
ValueFromPipeline = $true
)]
[ValidateNotNullOrEmpty()]
[string]$Message,
###
[Parameter(
Mandatory = $false,
HelpMessage = 'Information, Warning or Error.',
Position = 1,
ParameterSetName = 'Default',
ValueFromPipelineByPropertyName = $true
)]
[ValidateNotNullOrEmpty()]
[ValidateSet('Information', 'Warning', 'Error')]
[string]$Severity = 'Information',
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'End'
)]
[switch]$End,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'BeginFunction'
)]
[switch]$BeginFunction,
[Parameter(
Mandatory = $false,
ParameterSetName = 'EndFunction'
)]
[switch]$EndFunction,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'Start'
)]
[switch]$Start,
###
[Parameter(
Mandatory = $false,
ParameterSetName = 'End'
)]
[string]$OutputPath
)
begin {
$ErrorActionPreference = "SilentlyContinue"
# Define variables to hold information about the command that was invoked.
$ModuleName = $Script:MyInvocation.MyCommand.Name -replace '\..*'
$callStack = Get-PSCallStack
if ($callStack.Count -gt 1) {
$FuncName = $callStack[1].Command
} else {
$FuncName = "DirectCall" # Or any other default name you prefer
}
#Write-Verbose "Funcname Name is $FuncName!" -Verbose
$ModuleVer = $MyInvocation.MyCommand.Version.ToString()
# Set the error action preference to continue.
$ErrorActionPreference = "Continue"
}
process {
try {
if (-not $Start -and -not (Test-Path variable:script:LogString)) {
throw "The logging variable is not initialized. Please call Write-AuditLog with the -Start switch or ensure $script:LogString is set."
}
$Function = $($FuncName + '.v' + $ModuleVer)
if ($Start) {
$script:LogString = @()
$Message = '+++ Begin Log | ' + $Function + ' |'
}
elseif ($BeginFunction) {
$Message = '>>> Begin Function Log | ' + $Function + ' |'
}
$logEntry = [pscustomobject]@{
Time = ((Get-Date).ToString('yyyy-MM-dd hh:mmTss'))
Module = $ModuleName
PSVersion = ($PSVersionTable.PSVersion).ToString()
PSEdition = ($PSVersionTable.PSEdition).ToString()
IsAdmin = $(Test-IsAdmin)
User = "$Env:USERDOMAIN\$Env:USERNAME"
HostName = $Env:COMPUTERNAME
InvokedBy = $Function
Severity = $Severity
Message = $Message
RunID = -1
}
if ($BeginFunction) {
$maxRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Measure-Object -Property RunID -Maximum).Maximum
if ($null -eq $maxRunID) { $maxRunID = -1 }
$logEntry.RunID = $maxRunID + 1
}
else {
$lastRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Select-Object -Last 1).RunID
if ($null -eq $lastRunID) { $lastRunID = 0 }
$logEntry.RunID = $lastRunID
}
if ($EndFunction) {
$FunctionStart = "$((($script:LogString | Where-Object {$_.InvokedBy -eq $Function -and $_.RunId -eq $lastRunID } | Sort-Object Time)[0]).Time)"
$startTime = ([DateTime]::ParseExact("$FunctionStart", 'yyyy-MM-dd hh:mmTss', $null))
$endTime = Get-Date
$timeTaken = $endTime - $startTime
$Message = '<<< End Function Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
$logEntry.Message = $Message
}
elseif ($End) {
$startTime = ([DateTime]::ParseExact($($script:LogString[0].Time), 'yyyy-MM-dd hh:mmTss', $null))
$endTime = Get-Date
$timeTaken = $endTime - $startTime
$Message = '--- End Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
$logEntry.Message = $Message
}
$script:LogString += $logEntry
switch ($Severity) {
'Warning' {
Write-Warning ('[WARNING] ! ' + $Message)
$UserInput = Read-Host "Warning encountered! Do you want to continue? (Y/N)"
if ($UserInput -eq 'N') {
throw "Script execution stopped by user."
}
}
'Error' { Write-Error ('[ERROR] X - ' + $FuncName + ' ' + $Message) -ErrorAction Continue }
'Verbose' { Write-Verbose ('[VERBOSE] ~ ' + $Message) }
Default { Write-Information ('[INFO] * ' + $Message) -InformationAction Continue}
}
}
catch {
throw "Write-AuditLog encountered an error (process block): $($_)"
}
}
end {
try {
if ($End) {
if (-not [string]::IsNullOrEmpty($OutputPath)) {
$script:LogString | Export-Csv -Path $OutputPath -NoTypeInformation
Write-Verbose "LogPath: $(Split-Path -Path $OutputPath -Parent)"
}
else {
throw "OutputPath is not specified for End action."
}
}
}
catch {
throw "Error in Write-AuditLog (end block): $($_.Exception.Message)"
}
}
}

View File

@@ -25,6 +25,8 @@
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
#>
function Get-AdminRoleUserLicense {
# Set output type to System.Collections.ArrayList
[OutputType([System.Collections.ArrayList])]
[CmdletBinding()]
param (
[Parameter(Mandatory = $false)]

View File

@@ -5,8 +5,8 @@
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, such as compliance with CIS benchmarks.
.PARAMETER TenantAdminUrl
The URL of the tenant admin. This parameter is mandatory.
.PARAMETER DomainName
The domain name of the Microsoft 365 environment. This parameter is mandatory.
.PARAMETER M365DomainForPWPolicyTest
The domain name of the Microsoft 365 environment to test. This parameter is not mandatory and by default it will pass/fail all found domains as a group if a specific domain is not specified.
.PARAMETER ELevel
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
.PARAMETER ProfileLevel
@@ -59,40 +59,41 @@
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
#>
function Invoke-M365SecurityAudit {
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
[OutputType([CISAuditResult[]])]
param (
[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'.")]
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
[string]$TenantAdminUrl,
[Parameter(Mandatory = $true)]
[string]$DomainName,
[Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration policy when '1.3.1' is included in the tests to be run. The domain name of your organization, e.g., 'example.com'.")]
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
[string]$M365DomainForPWPolicyTest,
# E-Level with optional ProfileLevel selection
[Parameter(ParameterSetName = 'ELevelFilter')]
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
[ValidateSet('E3', 'E5')]
[string]$ELevel,
[Parameter(ParameterSetName = 'ELevelFilter')]
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
[ValidateSet('L1', 'L2')]
[string]$ProfileLevel,
# IG Filters, one at a time
[Parameter(ParameterSetName = 'IG1Filter')]
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')]
[switch]$IncludeIG1,
[Parameter(ParameterSetName = 'IG2Filter')]
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')]
[switch]$IncludeIG2,
[Parameter(ParameterSetName = 'IG3Filter')]
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')]
[switch]$IncludeIG3,
# Inclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'RecFilter')]
[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', `
'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', `
@@ -103,9 +104,9 @@ function Invoke-M365SecurityAudit {
[string[]]$IncludeRecommendation,
# Exclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'SkipRecFilter')]
[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', `
'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', `
@@ -121,129 +122,81 @@ function Invoke-M365SecurityAudit {
[switch]$NoModuleCheck
)
Begin {
if ($script:MaximumFunctionCount -lt 8192) {
$script:MaximumFunctionCount = 8192
}
# Ensure required modules are installed
# Define the required modules and versions in a hashtable
if (!($NoModuleCheck)) {
$requiredModules = @(
@{ 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" }
)
$requiredModules = Get-RequiredModule -AuditFunction
foreach ($module in $requiredModules) {
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
}
}
# Loop through each required module and assert its availability
# Establishing connections
#if (!($DoNotConnect -or $DoNotTest)) {
# Establishing connections
if (!($DoNotConnect)) {
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl
}
# Load test definitions from CSV
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath
# Load the Test Definitions into the script scope for use in other functions
$script:TestDefinitionsObject = $testDefinitions
# Apply filters based on parameter sets
switch ($PSCmdlet.ParameterSetName) {
'ELevelFilter' {
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
}
}
elseif ($null -ne $ELevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ELevel -eq $ELevel
}
}
elseif ($null -ne $ProfileLevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ProfileLevel -eq $ProfileLevel
}
}
}
'IG1Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
}
'IG2Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
}
'IG3Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
}
'RecFilter' {
$testDefinitions = $testDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
}
'SkipRecFilter' {
$testDefinitions = $testDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
}
}
# End switch ($PSCmdlet.ParameterSetName)
# Apply filters based on parameter sets
$params = @{
TestDefinitions = $testDefinitions
ParameterSetName = $PSCmdlet.ParameterSetName
ELevel = $ELevel
ProfileLevel = $ProfileLevel
IncludeRecommendation = $IncludeRecommendation
SkipRecommendation = $SkipRecommendation
}
$testDefinitions = Get-TestDefinitionsObject @params
# Extract unique connections needed
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
# Establishing connections if required
if (!($DoNotConnect)) {
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
}
# Determine which test files to load based on filtering
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
# Display the tests that would be loaded if the function is called with -WhatIf
Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:"
$testsToLoad | ForEach-Object { Write-Verbose " $_" }
# Initialize a collection to hold failed test details
$script:FailedTests = [System.Collections.ArrayList]::new()
} # End Begin
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
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
Where-Object { $testsToLoad -contains $_.BaseName }
$totalTests = $testFiles.Count
$currentTestIndex = 0
# Import the test functions
$testFiles | ForEach-Object {
$currentTestIndex++
Write-Progress -Activity "Loading Test Scripts" -Status "Loading $($currentTestIndex) of $($totalTests): $($_.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
Try {
# Dot source the test function
. $_.FullName
}
Catch {
# Log the error and add the test to the failed tests collection
Write-Error "Failed to load test function $($_.Name): $_"
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
}
}
$currentTestIndex = 0
# Execute each test function from the prepared list
foreach ($testFunction in $testFiles) {
$currentTestIndex++
Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
$functionName = $testFunction.BaseName
$functionCmd = Get-Command -Name $functionName
# Check if the test function needs DomainName parameter
$paramList = @{}
if ('DomainName' -in $functionCmd.Parameters.Keys) {
$paramList.DomainName = $DomainName
}
# Use splatting to pass parameters
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
Write-Host "Running $functionName..."
$result = & $functionName @paramList
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
[void]($allAuditResults.add($Result))
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $M365DomainForPWPolicyTest
# Add the result to the collection
[void]$allAuditResults.Add($auditResult)
}
}
}
@@ -251,10 +204,12 @@ function Invoke-M365SecurityAudit {
End {
if (!($DoNotDisconnect)) {
# Clean up sessions
Disconnect-M365Suite
Disconnect-M365Suite -RequiredConnections $requiredConnections
}
# Call the private function to calculate and display results
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
# Return all collected audit results
return $allAuditResults.ToArray()
# Check if the Disconnect switch is present
return $allAuditResults.ToArray() | Sort-Object -Property Rec
}
}

View File

@@ -1,14 +1,16 @@
<#
.SYNOPSIS
Synchronizes data between an Excel file and a CSV file and optionally updates the Excel worksheet.
Synchronizes data between an Excel file and either a CSV file or an output object from Invoke-M365SecurityAudit, and optionally updates the Excel worksheet.
.DESCRIPTION
The Sync-CISExcelAndCsvData function merges data from a specified Excel file and a CSV file based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file while preserving the original formatting and structure of the Excel worksheet.
The Sync-CISExcelAndCsvData function merges data from a specified Excel file with data from either a CSV file or an output object from Invoke-M365SecurityAudit based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file or audit results while preserving the original formatting and structure of the Excel worksheet.
.PARAMETER ExcelPath
The path to the Excel file that contains the original data. This parameter is mandatory.
.PARAMETER WorksheetName
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
.PARAMETER CsvPath
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory.
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory when using the CsvInput parameter set.
.PARAMETER AuditResults
An array of CISAuditResult objects from Invoke-M365SecurityAudit to be merged with the Excel data. This parameter is mandatory when using the ObjectInput parameter set.
.PARAMETER SkipUpdate
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
.EXAMPLE
@@ -17,6 +19,14 @@
.EXAMPLE
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
Retrieves the merged data object for preview without updating the Excel worksheet.
.EXAMPLE
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults
Merges data from the audit results into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
.EXAMPLE
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults -SkipUpdate
Retrieves the merged data object for preview without updating the Excel worksheet.
.INPUTS
None. You cannot pipe objects to Sync-CISExcelAndCsvData.
.OUTPUTS
@@ -29,26 +39,40 @@
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
#>
function Sync-CISExcelAndCsvData {
[CmdletBinding()]
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
param (
[Parameter(Mandatory = $true)]
[ValidateScript({ Test-Path $_ })]
[string]$ExcelPath,
[Parameter(Mandatory = $true)]
[string]$WorksheetName,
[Parameter(Mandatory = $true)]
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
[ValidateScript({ Test-Path $_ })]
[string]$CsvPath,
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput')]
[CISAuditResult[]]$AuditResults,
[Parameter(Mandatory = $false)]
[switch]$SkipUpdate
)
process {
# Merge Excel and CSV data
# Verify ImportExcel module is available
$requiredModules = Get-RequiredModule -SyncFunction
foreach ($module in $requiredModules) {
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
}
# Merge Excel and CSV data or Audit Results
if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
} else {
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -AuditResults $AuditResults
}
# Output the merged data if the user chooses to skip the update
if ($SkipUpdate) {
@@ -59,3 +83,5 @@ function Sync-CISExcelAndCsvData {
}
}
}

View File

@@ -1,5 +1,5 @@
Index,TestFileName,Rec,RecDescription,ELevel,ProfileLevel,CISControl,CISDescription,IG1,IG2,IG3,Automated,Connection
1,Test-AdministrativeAccountCompliance.ps1,1.1.1,Ensure Administrative accounts are separate and cloud-only,E3,L1,5.4,Restrict Administrator Privileges to Dedicated Administrator Accounts,TRUE,TRUE,TRUE,FALSE,AzureAD
1,Test-AdministrativeAccountCompliance.ps1,1.1.1,Ensure Administrative accounts are separate and cloud-only,E3,L1,5.4,Restrict Administrator Privileges to Dedicated Administrator Accounts,TRUE,TRUE,TRUE,FALSE,Microsoft Graph
2,Test-GlobalAdminsCount.ps1,1.1.3,Ensure that between two and four global admins are designated,E3,L1,5.1,Establish and Maintain an Inventory of Accounts,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
3,Test-ManagedApprovedPublicGroups.ps1,1.2.1,Ensure that only organizationally managed/approved public groups exist,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,AzureAD | EXO
@@ -18,8 +18,8 @@
17,Test-RestrictTenantCreation.ps1,5.1.2.3,Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes',E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Graph
18,Test-PasswordHashSync.ps1,5.1.8.1,Ensure password hash sync is enabled for hybrid deployments,E3,L1,6.7,Centralize Access Control,FALSE,TRUE,TRUE,TRUE,Microsoft Graph
19,Test-AuditDisabledFalse.ps1,6.1.1,Ensure 'AuditDisabled' organizationally is set to 'False',E3,L1,8.2,Collect Audit Logs,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
20,Test-MailboxAuditingE3.ps1,6.1.2,Ensure mailbox auditing for Office E3 users is Enabled,E3,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,EXO
21,Test-MailboxAuditingE5.ps1,6.1.3,Ensure mailbox auditing for Office E5 users is Enabled,E5,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,EXO
20,Test-MailboxAuditingE3.ps1,6.1.2,Ensure mailbox auditing for Office E3 users is Enabled,E3,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO | Microsoft Graph
21,Test-MailboxAuditingE5.ps1,6.1.3,Ensure mailbox auditing for Office E5 users is Enabled,E5,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO | Microsoft Graph
22,Test-BlockMailForwarding.ps1,6.2.1,Ensure all forms of mail forwarding are blocked and/or disabled,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
23,Test-NoWhitelistDomains.ps1,6.2.2,Ensure mail transport rules do not whitelist specific domains,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
24,Test-IdentifyExternalEmail.ps1,6.2.3,Ensure email from external senders is identified,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
1 Index TestFileName Rec RecDescription ELevel ProfileLevel CISControl CISDescription IG1 IG2 IG3 Automated Connection
2 1 Test-AdministrativeAccountCompliance.ps1 1.1.1 Ensure Administrative accounts are separate and cloud-only E3 L1 5.4 Restrict Administrator Privileges to Dedicated Administrator Accounts TRUE TRUE TRUE FALSE AzureAD Microsoft Graph
3 2 Test-GlobalAdminsCount.ps1 1.1.3 Ensure that between two and four global admins are designated E3 L1 5.1 Establish and Maintain an Inventory of Accounts TRUE TRUE TRUE TRUE Microsoft Graph
4 3 Test-ManagedApprovedPublicGroups.ps1 1.2.1 Ensure that only organizationally managed/approved public groups exist E3 L2 3.3 Configure Data Access Control Lists TRUE TRUE TRUE TRUE Microsoft Graph
5 4 Test-BlockSharedMailboxSignIn.ps1 1.2.2 Ensure sign-in to shared mailboxes is blocked E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE AzureAD | EXO
18 17 Test-RestrictTenantCreation.ps1 5.1.2.3 Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes' E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE Microsoft Graph
19 18 Test-PasswordHashSync.ps1 5.1.8.1 Ensure password hash sync is enabled for hybrid deployments E3 L1 6.7 Centralize Access Control FALSE TRUE TRUE TRUE Microsoft Graph
20 19 Test-AuditDisabledFalse.ps1 6.1.1 Ensure 'AuditDisabled' organizationally is set to 'False' E3 L1 8.2 Collect Audit Logs TRUE TRUE TRUE TRUE Microsoft Graph
21 20 Test-MailboxAuditingE3.ps1 6.1.2 Ensure mailbox auditing for Office E3 users is Enabled E3 L1 8.2 Collect audit logs. TRUE TRUE TRUE TRUE EXO AzureAD | EXO | Microsoft Graph
22 21 Test-MailboxAuditingE5.ps1 6.1.3 Ensure mailbox auditing for Office E5 users is Enabled E5 L1 8.2 Collect audit logs. TRUE TRUE TRUE TRUE EXO AzureAD | EXO | Microsoft Graph
23 22 Test-BlockMailForwarding.ps1 6.2.1 Ensure all forms of mail forwarding are blocked and/or disabled E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE EXO
24 23 Test-NoWhitelistDomains.ps1 6.2.2 Ensure mail transport rules do not whitelist specific domains E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE EXO
25 24 Test-IdentifyExternalEmail.ps1 6.2.3 Ensure email from external senders is identified E3 L1 0 Explicitly Not Mapped FALSE FALSE FALSE TRUE EXO

View File

@@ -1,26 +1,43 @@
function Test-AdministrativeAccountCompliance {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
)
begin {
#. .\source\Classes\CISAuditResult.ps1
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
$recnum = "1.1.1"
}
process {
try {
# Retrieve all necessary data outside the loops
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment
$principalIds = $roleAssignments.PrincipalId | Select-Object -Unique
# Fetch user details using filter
$userDetailsList = @{}
$licensesList = @{}
$userDetails = Get-MgUser -Filter "id in ('$($principalIds -join "','")')" -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
foreach ($user in $userDetails) {
$userDetailsList[$user.Id] = $user
}
# Fetch user licenses for each unique principal ID
foreach ($principalId in $principalIds) {
$licensesList[$principalId] = Get-MgUserLicenseDetail -UserId $principalId -ErrorAction SilentlyContinue
}
$adminRoleUsers = @()
foreach ($role in $adminRoles) {
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
foreach ($assignment in $roleAssignments) {
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
foreach ($assignment in $roleAssignments | Where-Object { $_.RoleDefinitionId -eq $role.Id }) {
$userDetails = $userDetailsList[$assignment.PrincipalId]
if ($userDetails) {
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
$licenses = $licensesList[$assignment.PrincipalId]
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
$adminRoleUsers += [PSCustomObject]@{
@@ -50,23 +67,27 @@ function Test-AdministrativeAccountCompliance {
$failureReasons = $nonCompliantUsers | ForEach-Object {
$accountType = if ($_.HybridUser) { "Hybrid" } else { "Cloud-Only" }
$missingLicenses = $validLicenses | Where-Object { $_ -notin ($_.Licenses -split '\|') }
"$($_.UserName)|$($_.Roles)|$accountType|Missing: $($missingLicenses -join ',')"
"$($_.UserName)|$($_.Roles)|$accountType|$($missingLicenses -join ',')"
}
$failureReasons = $failureReasons -join "`n"
$details = if ($nonCompliantUsers) {
"Non-Compliant Accounts: $($nonCompliantUsers.Count)`nDetails:`n" + ($nonCompliantUsers | ForEach-Object { $_.UserName }) -join "`n"
}
else {
"Non-compliant accounts: `nUsername | Roles | HybridStatus | Missing Licence`n$failureReasons"
} else {
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
}
$failureReason = if ($nonCompliantUsers) {
"Non-Compliant Accounts: $($nonCompliantUsers.Count)`nDetails:`n" + ($nonCompliantUsers | ForEach-Object { $_.UserName }) -join "`n"
} else {
"N/A"
}
$result = $nonCompliantUsers.Count -eq 0
$status = if ($result) { 'Pass' } else { 'Fail' }
$failureReason = if ($nonCompliantUsers) { "Non-compliant accounts: `nUsername | Roles | HybridStatus | Missing Licence`n$failureReasons" } else { "N/A" }
# Create the parameter splat
$params = @{
Rec = "1.1.1"
Rec = $recnum
Result = $result
Status = $status
Details = $details
@@ -75,9 +96,18 @@ function Test-AdministrativeAccountCompliance {
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Output the result
return $auditResult
}
}

View File

@@ -1,5 +1,6 @@
function Test-AntiPhishingPolicy {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -10,9 +11,12 @@ function Test-AntiPhishingPolicy {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
#$auditResults = @()
$recnum = "2.1.7"
}
process {
try {
# 2.1.7 Ensure that an anti-phishing policy has been created
# Retrieve and validate the anti-phishing policies
@@ -61,7 +65,7 @@ function Test-AntiPhishingPolicy {
# Parameter splat for Initialize-CISAuditResult function
$params = @{
Rec = "2.1.7"
Rec = $recnum
Result = $nonCompliantItems.Count -eq 0
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = $details
@@ -71,6 +75,19 @@ function Test-AntiPhishingPolicy {
# Create and populate the CISAuditResult object
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-AuditDisabledFalse {
[CmdletBinding()]
[OutputType([CISAuditResult])]
# Aligned
param (
# Parameters can be added if needed
@@ -9,9 +10,12 @@ function Test-AuditDisabledFalse {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.1.1"
}
process {
try {
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
# Retrieve the AuditDisabled configuration
@@ -35,7 +39,7 @@ function Test-AuditDisabledFalse {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.1.1"
Rec = $recnum
Result = $auditNotDisabled
Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +47,19 @@ function Test-AuditDisabledFalse {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-AuditLogSearch {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,12 @@ function Test-AuditLogSearch {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "3.1.1"
}
process {
try {
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
# Retrieve the audit log configuration
@@ -35,14 +39,26 @@ function Test-AuditLogSearch {
# Create and populate the CISAuditResult object
$params = @{
Rec = "3.1.1"
Rec = $recnum
Result = $auditLogResult
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {

View File

@@ -1,5 +1,6 @@
function Test-BlockChannelEmails {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added here if needed
@@ -9,9 +10,12 @@ function Test-BlockChannelEmails {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.1.2"
}
process {
try {
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
# Retrieve Teams client configuration
@@ -35,7 +39,7 @@ function Test-BlockChannelEmails {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.1.2"
Rec = $recnum
Result = -not $allowEmailIntoChannel
Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +47,19 @@ function Test-BlockChannelEmails {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,7 +1,7 @@
function Test-BlockMailForwarding {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned Compare
# Parameters can be added if needed
)
@@ -9,34 +9,59 @@ function Test-BlockMailForwarding {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.2.1"
}
process {
try {
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
# Retrieve the transport rules that redirect messages
# Step 1: Retrieve the transport rules that redirect messages
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
$forwardingBlocked = $transportRules.Count -eq 0
$transportForwardingBlocked = $transportRules.Count -eq 0
# Step 2: Check all anti-spam outbound policies
$outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy
$nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' }
$nonCompliantSpamPoliciesArray = @($nonCompliantSpamPolicies)
$spamForwardingBlocked = $nonCompliantSpamPoliciesArray.Count -eq 0
# Determine overall compliance
$forwardingBlocked = $transportForwardingBlocked -and $spamForwardingBlocked
# Prepare failure reasons and details based on compliance
$failureReasons = if ($transportRules.Count -gt 0) {
"Mail forwarding rules found: $($transportRules.Name -join ', ')"
$failureReasons = @()
$details = @()
if ($transportRules.Count -gt 0) {
$failureReasons += "Mail forwarding rules found: $($transportRules.Name -join ', ')"
$details += "Transport Rules Details:`nRule Name|Redirects To"
$details += $transportRules | ForEach-Object {
"$($_.Name)|$($_.RedirectMessageTo -join ', ')"
}
else {
"N/A"
$details += "`n"
}
$details = if ($transportRules.Count -gt 0) {
$transportRules | ForEach-Object {
"$($_.Name) redirects to $($_.RedirectMessageTo)"
} -join " | "
if ($nonCompliantSpamPoliciesArray.Count -gt 0) {
$failureReasons += "Outbound spam policies allowing automatic forwarding found."
$details += "Outbound Spam Policies Details:`nPolicy|AutoForwardingMode"
$details += $nonCompliantSpamPoliciesArray | ForEach-Object {
"$($_.Name)|$($_.AutoForwardingMode)"
}
else {
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark."
}
if ($failureReasons.Count -eq 0) {
$failureReasons = "N/A"
$details = "Both transport rules and outbound spam policies are configured correctly to block forwarding."
}
else {
$failureReasons = $failureReasons -join " | "
$details = $details -join "`n"
}
# Populate the audit result
$params = @{
Rec = "6.2.1"
Rec = $recnum
Result = $forwardingBlocked
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
Details = $details
@@ -44,6 +69,19 @@ function Test-BlockMailForwarding {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-BlockSharedMailboxSignIn {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,12 @@ function Test-BlockSharedMailboxSignIn {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.2.2"
}
process {
try {
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
# Retrieve shared mailbox details
@@ -37,7 +41,7 @@ function Test-BlockSharedMailboxSignIn {
# Create and populate the CISAuditResult object
$params = @{
Rec = "1.2.2"
Rec = $recnum
Result = $allBlocked
Status = if ($allBlocked) { "Pass" } else { "Fail" }
Details = $details
@@ -45,6 +49,19 @@ function Test-BlockSharedMailboxSignIn {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-CommonAttachmentFilter {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,11 @@ function Test-CommonAttachmentFilter {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.2"
}
process {
try {
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
# Retrieve the attachment filter policy
@@ -35,7 +38,7 @@ function Test-CommonAttachmentFilter {
# Create and populate the CISAuditResult object
$params = @{
Rec = "2.1.2"
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +46,19 @@ function Test-CommonAttachmentFilter {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-CustomerLockbox {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
@@ -9,9 +10,12 @@ function Test-CustomerLockbox {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.3.6"
}
process {
try {
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled
# Retrieve the organization configuration
@@ -35,7 +39,7 @@ function Test-CustomerLockbox {
# Create and populate the CISAuditResult object #
$params = @{
Rec = "1.3.6"
Rec = $recnum
Result = $customerLockboxEnabled
Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +47,19 @@ function Test-CustomerLockbox {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-DialInBypassLobby {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,12 @@ function Test-DialInBypassLobby {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.4"
}
process {
try {
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
# Retrieve Teams meeting policy for PSTN users
@@ -35,7 +39,7 @@ function Test-DialInBypassLobby {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.4"
Rec = $recnum
Result = $PSTNBypassDisabled
Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +47,19 @@ function Test-DialInBypassLobby {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-DisallowInfectedFilesDownload {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
@@ -10,9 +11,12 @@ function Test-DisallowInfectedFilesDownload {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.3.1"
}
process {
try {
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
# Retrieve the SharePoint tenant configuration
@@ -36,14 +40,26 @@ function Test-DisallowInfectedFilesDownload {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.3.1"
Rec = $recnum
Result = $isDisallowInfectedFileDownloadEnabled
Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {

View File

@@ -1,5 +1,6 @@
function Test-EnableDKIM {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,12 @@ function Test-EnableDKIM {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.9"
}
process {
try {
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
# Retrieve DKIM configuration for all domains
@@ -36,7 +40,7 @@ function Test-EnableDKIM {
# Create and populate the CISAuditResult object
$params = @{
Rec = "2.1.9"
Rec = $recnum
Result = $dkimResult
Status = if ($dkimResult) { "Pass" } else { "Fail" }
Details = $details
@@ -44,6 +48,19 @@ function Test-EnableDKIM {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-ExternalNoControl {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -10,9 +11,12 @@ function Test-ExternalNoControl {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.7"
}
process {
try {
# 8.5.7 (L1) Ensure external participants can't give or request control
# Retrieve Teams meeting policy for external participant control
@@ -36,7 +40,7 @@ function Test-ExternalNoControl {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.7"
Rec = $recnum
Result = $externalControlRestricted
Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -44,6 +48,19 @@ function Test-ExternalNoControl {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-ExternalSharingCalendars {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -10,9 +11,12 @@ function Test-ExternalSharingCalendars {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.3.3"
}
process {
try {
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
# Retrieve sharing policies related to calendar sharing
@@ -45,7 +49,7 @@ function Test-ExternalSharingCalendars {
# Create and populate the CISAuditResult object
$params = @{
Rec = "1.3.3"
Rec = $recnum
Result = $isExternalSharingDisabled
Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
Details = $details
@@ -53,6 +57,19 @@ function Test-ExternalSharingCalendars {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-GlobalAdminsCount {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
@@ -10,16 +11,20 @@ function Test-GlobalAdminsCount {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.1.3"
}
process {
try {
# 1.1.3 (L1) Ensure that between two and four global admins are designated
# Retrieve global admin role and members
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
$globalAdminCount = $globalAdmins.Count
$globalAdminUsernames = ($globalAdmins | ForEach-Object {
"$($_.AdditionalProperties["displayName"]) ($($_.AdditionalProperties["userPrincipalName"]))"
}) -join ', '
# Prepare failure reasons and details based on compliance
$failureReasons = if ($globalAdminCount -lt 2) {
@@ -36,7 +41,7 @@ function Test-GlobalAdminsCount {
# Create and populate the CISAuditResult object
$params = @{
Rec = "1.1.3"
Rec = $recnum
Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
Details = $details
@@ -44,6 +49,19 @@ function Test-GlobalAdminsCount {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-GuestAccessExpiration {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
@@ -10,9 +11,12 @@ function Test-GuestAccessExpiration {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.9"
}
process {
try {
# 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
# Retrieve SharePoint tenant settings related to guest access expiration
@@ -31,7 +35,7 @@ function Test-GuestAccessExpiration {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.2.9"
Rec = $recnum
Result = $isGuestAccessExpirationConfiguredCorrectly
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
Details = $details
@@ -39,6 +43,19 @@ function Test-GuestAccessExpiration {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-GuestUsersBiweeklyReview {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
@@ -10,9 +11,11 @@ function Test-GuestUsersBiweeklyReview {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.1.4"
}
process {
try {
# 1.1.4 (L1) Ensure Guest Users are reviewed at least biweekly
@@ -38,7 +41,7 @@ function Test-GuestUsersBiweeklyReview {
# Create and populate the CISAuditResult object
$params = @{
Rec = "1.1.4"
Rec = $recnum
Result = -not $guestUsers
Status = if ($guestUsers) { "Fail" } else { "Pass" }
Details = $details
@@ -46,6 +49,19 @@ function Test-GuestUsersBiweeklyReview {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-IdentifyExternalEmail {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -10,9 +11,12 @@ function Test-IdentifyExternalEmail {
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.2.3"
}
process {
try {
# 6.2.3 (L1) Ensure email from external senders is identified
# Retrieve external sender tagging configuration
@@ -31,7 +35,7 @@ function Test-IdentifyExternalEmail {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.2.3"
Rec = $recnum
Result = $externalTaggingEnabled
Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
Details = $details
@@ -39,6 +43,19 @@ function Test-IdentifyExternalEmail {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-LinkSharingRestrictions {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -10,9 +11,11 @@ function Test-LinkSharingRestrictions {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.7"
}
process {
try {
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
# Retrieve link sharing configuration for SharePoint and OneDrive
@@ -31,13 +34,27 @@ function Test-LinkSharingRestrictions {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.2.7"
Rec = $recnum
Result = $isLinkSharingRestricted
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {

View File

@@ -1,5 +1,6 @@
function Test-MailTipsEnabled {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -11,9 +12,11 @@ function Test-MailTipsEnabled {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "6.5.2"
}
process {
try {
# 6.5.2 (L2) Ensure MailTips are enabled for end users
# Retrieve organization configuration for MailTips settings
@@ -38,7 +41,7 @@ function Test-MailTipsEnabled {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.5.2"
Rec = $recnum
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
Details = $details
@@ -46,6 +49,19 @@ function Test-MailTipsEnabled {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,8 +1,7 @@
function Test-MailboxAuditingE3 {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Create Table for Details
# Parameters can be added if needed
)
@@ -15,19 +14,20 @@ function Test-MailboxAuditingE3 {
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$allFailures = @()
$allUsers = Get-AzureADUser -All $true
$processedUsers = @{} # Dictionary to track processed users
$recnum = "6.1.2"
}
process {
try {
foreach ($user in $allUsers) {
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
continue
}
try {
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
@@ -47,32 +47,33 @@ function Test-MailboxAuditingE3 {
foreach ($action in $OwnerActions) {
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
}
if ($missingActions.Count -gt 0) {
$formattedActions = Format-MissingAction -missingActions $missingActions
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
}
}
else {
$allFailures += "$userUPN`: AuditEnabled - False"
continue
$allFailures += "$userUPN|False|||"
}
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
# Mark the user as processed
$processedUsers[$user.UserPrincipalName] = $true
}
}
catch {
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
}
}
# Prepare failure reasons and details based on compliance
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
$details = if ($allFailures.Count -eq 0) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
$details = if ($allFailures.Count -eq 0) {
"All Office E3 users have correct mailbox audit settings."
}
else {
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
}
# Populate the audit result
$params = @{
Rec = "6.1.2"
Rec = $recnum
Result = $allFailures.Count -eq 0
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
Details = $details
@@ -80,35 +81,29 @@ function Test-MailboxAuditingE3 {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
#$verbosePreference = 'Continue'
$detailsLength = $details.Length
Write-Verbose "Character count of the details: $detailsLength"
if ($detailsLength -gt 32767) {
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
}
#$verbosePreference = 'SilentlyContinue'
return $auditResult
}
}
function Format-MissingActions {
param ([array]$missingActions)
$actionGroups = @{
"Admin" = @()
"Delegate" = @()
"Owner" = @()
}
foreach ($action in $missingActions) {
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
$type = $matches[1]
$actionName = $matches[2]
$actionGroups[$type] += $actionName
}
}
$formattedResults = @()
foreach ($type in $actionGroups.Keys) {
if ($actionGroups[$type].Count -gt 0) {
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
}
}
return $formattedResults -join '; '
}

View File

@@ -1,8 +1,7 @@
function Test-MailboxAuditingE5 {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Create Table for Details
# Parameters can be added if needed
)
@@ -15,23 +14,24 @@ function Test-MailboxAuditingE5 {
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$allFailures = @()
$allUsers = Get-AzureADUser -All $true
$processedUsers = @{} # Dictionary to track processed users
$recnum = "6.1.3"
}
process {
try {
foreach ($user in $allUsers) {
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
continue
}
try {
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
if ($hasOfficeE5) {
$userUPN = $user.UserPrincipalName
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
@@ -47,38 +47,33 @@ function Test-MailboxAuditingE5 {
foreach ($action in $OwnerActions) {
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
}
if ($missingActions.Count -gt 0) {
$formattedActions = Format-MissingAction -missingActions $missingActions
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
}
}
else {
$allFailures += "$userUPN`: AuditEnabled - False"
continue
$allFailures += "$userUPN|False|||"
}
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
else {
Write-Verbose "User $($user.UserPrincipalName) passed the mailbox audit checks."
}
# Mark the user as processed
$processedUsers[$user.UserPrincipalName] = $true
}
else {
# Adding verbose output to indicate the user does not have an E5 license
Write-Verbose "User $($user.UserPrincipalName) does not have an Office E5 license."
}
}
catch {
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
}
}
# Prepare failure reasons and details based on compliance
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
$details = if ($allFailures.Count -eq 0) { "All Office E5 users have correct mailbox audit settings." } else { $allFailures -join " | " }
$details = if ($allFailures.Count -eq 0) {
"All Office E5 users have correct mailbox audit settings."
}
else {
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
}
# Populate the audit result
$params = @{
Rec = "6.1.3"
Rec = $recnum
Result = $allFailures.Count -eq 0
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
Details = $details
@@ -86,35 +81,29 @@ function Test-MailboxAuditingE5 {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
#$verbosePreference = 'Continue'
$detailsLength = $details.Length
Write-Verbose "Character count of the details: $detailsLength"
if ($detailsLength -gt 32767) {
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
}
#$verbosePreference = 'SilentlyContinue'
return $auditResult
}
}
function Format-MissingActions {
param ([array]$missingActions)
$actionGroups = @{
"Admin" = @()
"Delegate" = @()
"Owner" = @()
}
foreach ($action in $missingActions) {
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
$type = $matches[1]
$actionName = $matches[2]
$actionGroups[$type] += $actionName
}
}
$formattedResults = @()
foreach ($type in $actionGroups.Keys) {
if ($actionGroups[$type].Count -gt 0) {
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
}
}
return $formattedResults -join '; '
}

View File

@@ -1,5 +1,6 @@
function Test-ManagedApprovedPublicGroups {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,10 +10,11 @@ function Test-ManagedApprovedPublicGroups {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.2.1"
}
process {
try {
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
# Retrieve all public groups
@@ -36,7 +38,7 @@ function Test-ManagedApprovedPublicGroups {
# Create and populate the CISAuditResult object
$params = @{
Rec = "1.2.1"
Rec = $recnum
Result = $null -eq $allGroups -or $allGroups.Count -eq 0
Status = if ($null -eq $allGroups -or $allGroups.Count -eq 0) { "Pass" } else { "Fail" }
Details = $details
@@ -44,6 +46,19 @@ function Test-ManagedApprovedPublicGroups {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResults

View File

@@ -1,5 +1,6 @@
function Test-MeetingChatNoAnonymous {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,11 @@ function Test-MeetingChatNoAnonymous {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.5"
}
process {
try {
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -32,7 +35,7 @@ function Test-MeetingChatNoAnonymous {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.5"
Rec = $recnum
Result = $chatAnonDisabled
Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
Details = $details
@@ -40,6 +43,19 @@ function Test-MeetingChatNoAnonymous {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-ModernAuthExchangeOnline {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,6 +10,7 @@ function Test-ModernAuthExchangeOnline {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.5.1"
}
process {
@@ -30,7 +32,7 @@ function Test-ModernAuthExchangeOnline {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.5.1"
Rec = $recnum
Result = $orgConfig.OAuth2ClientProfileEnabled
Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
Details = $details
@@ -40,8 +42,18 @@ function Test-ModernAuthExchangeOnline {
}
catch {
Write-Error "An error occurred while testing modern authentication for Exchange Online: $_"
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {

View File

@@ -1,5 +1,6 @@
function Test-ModernAuthSharePoint {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,9 +10,11 @@ function Test-ModernAuthSharePoint {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.1"
}
process {
try {
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
@@ -28,7 +31,7 @@ function Test-ModernAuthSharePoint {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.2.1"
Rec = $recnum
Result = $modernAuthForSPRequired
Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
Details = $details
@@ -36,6 +39,19 @@ function Test-ModernAuthSharePoint {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-NoAnonymousMeetingJoin {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,11 @@ function Test-NoAnonymousMeetingJoin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.1"
}
process {
try {
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -31,7 +34,7 @@ function Test-NoAnonymousMeetingJoin {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.1"
Rec = $recnum
Result = -not $allowAnonymousUsersToJoinMeeting
Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
Details = $details
@@ -39,6 +42,19 @@ function Test-NoAnonymousMeetingJoin {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-NoAnonymousMeetingStart {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,11 @@ function Test-NoAnonymousMeetingStart {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.2"
}
process {
try {
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -31,7 +34,7 @@ function Test-NoAnonymousMeetingStart {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.2"
Rec = $recnum
Result = $anonymousStartDisabled
Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
Details = $details
@@ -39,6 +42,19 @@ function Test-NoAnonymousMeetingStart {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-NoWhitelistDomains {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,9 +10,11 @@ function Test-NoWhitelistDomains {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.2.2"
}
process {
try {
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
# Retrieve transport rules that whitelist specific domains
@@ -35,7 +38,7 @@ function Test-NoWhitelistDomains {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.2.2"
Rec = $recnum
Result = -not $whitelistedRules
Status = if ($whitelistedRules) { "Fail" } else { "Pass" }
Details = $details
@@ -43,6 +46,19 @@ function Test-NoWhitelistDomains {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-NotifyMalwareInternal {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,11 @@ function Test-NotifyMalwareInternal {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.3"
}
process {
try {
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
# Retrieve all 'Custom' malware filter policies and check notification settings
@@ -44,7 +47,7 @@ function Test-NotifyMalwareInternal {
# Create and populate the CISAuditResult object
$params = @{
Rec = "2.1.3"
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
@@ -52,6 +55,19 @@ function Test-NotifyMalwareInternal {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-OneDriveContentRestrictions {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,9 +10,11 @@ function Test-OneDriveContentRestrictions {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.4"
}
process {
try {
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
# Retrieve OneDrive sharing capability settings
@@ -35,7 +38,7 @@ function Test-OneDriveContentRestrictions {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.2.4"
Rec = $recnum
Result = $isOneDriveSharingRestricted
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +46,19 @@ function Test-OneDriveContentRestrictions {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-OneDriveSyncRestrictions {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,9 +10,11 @@ function Test-OneDriveSyncRestrictions {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.3.2"
}
process {
try {
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
# Retrieve OneDrive sync client restriction settings
@@ -35,7 +38,7 @@ function Test-OneDriveSyncRestrictions {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.3.2"
Rec = $recnum
Result = $isSyncRestricted
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +46,19 @@ function Test-OneDriveSyncRestrictions {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-OrgOnlyBypassLobby {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,11 @@ function Test-OrgOnlyBypassLobby {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.3"
}
process {
try {
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -37,7 +40,7 @@ function Test-OrgOnlyBypassLobby {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.3"
Rec = $recnum
Result = $lobbyBypassRestricted
Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -45,6 +48,19 @@ function Test-OrgOnlyBypassLobby {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-OrganizersPresent {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,11 @@ function Test-OrganizersPresent {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.6"
}
process {
try {
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -37,7 +40,7 @@ function Test-OrganizersPresent {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.5.6"
Rec = $recnum
Result = $presenterRoleRestricted
Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -45,6 +48,19 @@ function Test-OrganizersPresent {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-PasswordHashSync {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,11 @@ function Test-PasswordHashSync {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "5.1.8.1"
}
process {
try {
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
@@ -31,7 +34,7 @@ function Test-PasswordHashSync {
# Create and populate the CISAuditResult object
$params = @{
Rec = "5.1.8.1"
Rec = $recnum
Result = $hashSyncResult
Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
Details = $details
@@ -39,6 +42,19 @@ function Test-PasswordHashSync {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,44 +1,84 @@
function Test-PasswordNeverExpirePolicy {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
[Parameter(Mandatory)]
[string]$DomainName # DomainName parameter is now mandatory
[Parameter(Mandatory = $false)]
[string]$DomainName
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.3.1"
$overallResult = $true
$detailsList = @()
$failureReasonsList = @()
# Add headers for the details
$detailsList += "Domain|Validity Period|IsDefault"
}
process {
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
# Pass if PasswordValidityPeriodInDays is 0. Fail otherwise.
try {
# Retrieve all domains or a specific domain
$domains = if ($DomainName) {
Get-MgDomain -DomainId $DomainName
} else {
Get-MgDomain
}
foreach ($domain in $domains) {
$domainName = $domain.Id
$isDefault = $domain.IsDefault
# Retrieve password expiration policy
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object -ExpandProperty PasswordValidityPeriodInDays
$passwordPolicy = $domain.PasswordValidityPeriodInDays
# Determine if the policy is compliant
$isCompliant = $passwordPolicy -eq 0
$overallResult = $overallResult -and $isCompliant
# Prepare failure reasons and details based on compliance
$failureReasons = if ($passwordPolicy -ne 0) {
"Password expiration is not set to never expire"
}
else {
$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"
}
$details = "Validity Period: $passwordPolicy days"
$details = "$domainName|$passwordPolicy days|$isDefault"
# Add details and failure reasons to the lists
$detailsList += $details
$failureReasonsList += $failureReasons
}
# Prepare the final failure reason and details
$finalFailureReason = $failureReasonsList -join "`n"
$finalDetails = $detailsList -join "`n"
# Create and populate the CISAuditResult object
$params = @{
Rec = "1.3.1"
Result = $passwordPolicy -eq 0
Status = if ($passwordPolicy -eq 0) { "Pass" } else { "Fail" }
Details = $details
FailureReason = $failureReasons
Rec = $recnum
Result = $overallResult
Status = if ($overallResult) { "Pass" } else { "Fail" }
Details = $finalDetails
FailureReason = $finalFailureReason
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-ReauthWithCode {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,9 +10,11 @@ function Test-ReauthWithCode {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.10"
}
process {
try {
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
# Retrieve reauthentication settings for SharePoint Online
@@ -30,7 +33,7 @@ function Test-ReauthWithCode {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.2.10"
Rec = $recnum
Result = $isReauthenticationRestricted
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -38,6 +41,19 @@ function Test-ReauthWithCode {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-ReportSecurityInTeams {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -9,9 +10,11 @@ function Test-ReportSecurityInTeams {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.6.1"
}
process {
try {
# 8.6.1 (L1) Ensure users can report security concerns in Teams
# Retrieve the necessary settings for Teams and Exchange Online
@@ -40,7 +43,7 @@ function Test-ReportSecurityInTeams {
# Create and populate the CISAuditResult object
$params = @{
Rec = "8.6.1"
Rec = $recnum
Result = $securityReportEnabled
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
Details = $details
@@ -48,6 +51,19 @@ function Test-ReportSecurityInTeams {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,36 +1,70 @@
function Test-RestrictCustomScripts {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# . .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.3.4"
}
process {
try {
# 7.3.4 (L1) Ensure custom script execution is restricted on site collections
# Retrieve all site collections and select necessary properties
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
$customScriptAllowedSites = $SPOSitesCustomScript | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
# Replace 'sharepoint.com' with '<SPUrl>'
$processedUrls = $SPOSitesCustomScript | ForEach-Object {
$_.Url = $_.Url -replace 'sharepoint\.com', '<SPUrl>'
$_
}
# Find sites where custom scripts are allowed
$customScriptAllowedSites = $processedUrls | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
#$verbosePreference = 'Continue'
# Check the total length of URLs
$totalUrlLength = ($customScriptAllowedSites.Url -join '').Length
Write-Verbose "Total length of URLs: $totalUrlLength"
# Extract hostnames from allowed sites if the total length exceeds the limit
$mostUsedHostname = $null
if ($totalUrlLength -gt 20000) {
Write-Verbose "Extracting hostnames from URLs..."
$hostnames = $customScriptAllowedSites.Url | ForEach-Object {
if ($_ -match '^https://([^\.]+)\.') {
$matches[1]
}
}
Write-Verbose "Extracted hostnames: $($hostnames -join ', ')"
# Find the most used hostname using the Get-MostCommonWord function
$mostUsedHostname = Get-MostCommonWord -InputStrings $hostnames
Write-Verbose "Most used hostname: $mostUsedHostname"
}
#$verbosePreference = 'SilentlyContinue'
# Compliance is true if no sites allow custom scripts
$complianceResult = $customScriptAllowedSites.Count -eq 0
# Gather details for non-compliant sites (where custom scripts are allowed)
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object {
"$($_.Title) ($($_.Url)): Custom Script Allowed"
$url = $_.Url
if ($null -ne $mostUsedHostname -and $url -match "^https://$mostUsedHostname\.<SPUrl>") {
$url = $url -replace "^https://$mostUsedHostname\.<SPUrl>", "https://<corp>.<SPUrl>"
}
"$(if ($_.Title) {$_.Title} else {"NoTitle"})|$url"
}
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $complianceResult) {
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
"Some site collections are not restricting custom script execution. Review Details property for sites that are not aligned with the benchmark."
}
else {
"N/A"
@@ -40,12 +74,22 @@ function Test-RestrictCustomScripts {
"All site collections have custom script execution restricted"
}
else {
$nonCompliantSiteDetails -join "; "
"Title|Url`n" + ($nonCompliantSiteDetails -join "`n")
}
# Convert details to PSObject and check length
$detailsPSObject = $details | ConvertFrom-Csv -Delimiter '|'
$detailsLength = ($detailsPSObject | ForEach-Object { $_.Url }).Length
if ($detailsLength -gt 32767) {
# Create a preview of the first 10 results
$preview = $detailsPSObject | Select-Object -First 10 | ForEach-Object { "$($_.Title)|$($_.Url)" }
$details = "The output is too large. Here is a preview of the first 10 results:`n`n" + ($preview -join "`n") + "`n`nPlease run the test with the following commands to get the full details:`n`nGet-SPOSite -Limit All | Where-Object { `$.DenyAddAndCustomizePages -ne 'Enabled' } | Select-Object Title, Url"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.3.4"
Rec = $recnum
Result = $complianceResult
Status = if ($complianceResult) { "Pass" } else { "Fail" }
Details = $details
@@ -53,8 +97,30 @@ function Test-RestrictCustomScripts {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Measure the character count of the details
#$verbosePreference = 'Continue'
$detailsLength = $details.Length
Write-Verbose "Character count of the details: $detailsLength"
if ($detailsLength -gt 32767) {
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
}
#$verbosePreference = 'SilentlyContinue'
# Return auditResult
return $auditResult
}

View File

@@ -1,5 +1,6 @@
function Test-RestrictExternalSharing {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -9,9 +10,11 @@ function Test-RestrictExternalSharing {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "7.2.3"
}
process {
try {
# 7.2.3 (L1) Ensure external content sharing is restricted
# Retrieve the SharingCapability setting for the SharePoint tenant
@@ -30,7 +33,7 @@ function Test-RestrictExternalSharing {
# Create and populate the CISAuditResult object
$params = @{
Rec = "7.2.3"
Rec = $recnum
Result = $isRestricted
Status = if ($isRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -38,6 +41,19 @@ function Test-RestrictExternalSharing {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-RestrictOutlookAddins {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters could include credentials or other necessary data
@@ -12,9 +13,11 @@ function Test-RestrictOutlookAddins {
$customPolicyFailures = @()
$defaultPolicyFailureDetails = @()
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
$recnum = "6.3.1"
}
process {
try {
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
# Check all mailboxes for custom policies with unallowed add-ins
@@ -62,7 +65,7 @@ function Test-RestrictOutlookAddins {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.3.1"
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = $detailsString
@@ -70,6 +73,19 @@ function Test-RestrictOutlookAddins {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-RestrictStorageProvidersOutlook {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added here if needed
@@ -9,9 +10,11 @@ function Test-RestrictStorageProvidersOutlook {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "6.5.3"
}
process {
try {
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
# Retrieve all OwaMailbox policies
@@ -38,7 +41,7 @@ function Test-RestrictStorageProvidersOutlook {
# Create and populate the CISAuditResult object
$params = @{
Rec = "6.5.3"
Rec = $recnum
Result = $allPoliciesRestricted
Status = if ($allPoliciesRestricted) { "Pass" } else { "Fail" }
Details = $details
@@ -46,6 +49,19 @@ function Test-RestrictStorageProvidersOutlook {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-RestrictTenantCreation {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,11 @@ function Test-RestrictTenantCreation {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "5.1.2.3"
}
process {
try {
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
# Retrieve the tenant creation policy
@@ -30,7 +33,7 @@ function Test-RestrictTenantCreation {
# Create and populate the CISAuditResult object
$params = @{
Rec = "5.1.2.3"
Rec = $recnum
Result = $tenantCreationResult
Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
Details = $details
@@ -38,6 +41,19 @@ function Test-RestrictTenantCreation {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-SafeAttachmentsPolicy {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,11 @@ function Test-SafeAttachmentsPolicy {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.4"
}
process {
try {
# 2.1.4 (L2) Ensure Safe Attachments policy is enabled
# Retrieve all Safe Attachment policies where Enable is set to True
@@ -35,7 +38,7 @@ function Test-SafeAttachmentsPolicy {
# Create and populate the CISAuditResult object
$params = @{
Rec = "2.1.4"
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
@@ -43,6 +46,19 @@ function Test-SafeAttachmentsPolicy {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-SafeAttachmentsTeams {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -9,9 +10,11 @@ function Test-SafeAttachmentsTeams {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.5"
}
process {
try {
# 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
@@ -42,7 +45,7 @@ function Test-SafeAttachmentsTeams {
# Create and populate the CISAuditResult object
$params = @{
Rec = "2.1.5"
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
@@ -50,6 +53,19 @@ function Test-SafeAttachmentsTeams {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-SafeLinksOfficeApps {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here if needed
@@ -9,9 +10,11 @@ function Test-SafeLinksOfficeApps {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "2.1.1"
}
process {
try {
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
# Retrieve all Safe Links policies
@@ -49,7 +52,7 @@ function Test-SafeLinksOfficeApps {
# Create and populate the CISAuditResult object
$params = @{
Rec = "2.1.1"
Rec = $recnum
Result = $result
Status = if ($result) { "Pass" } else { "Fail" }
Details = $details
@@ -57,6 +60,19 @@ function Test-SafeLinksOfficeApps {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return the audit result

View File

@@ -1,5 +1,6 @@
function Test-SharePointAADB2B {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -11,15 +12,17 @@ function Test-SharePointAADB2B {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "7.2.2"
}
process {
try {
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
# Populate the auditResult object with the required properties
$params = @{
Rec = "7.2.2"
Rec = $recnum
Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
@@ -27,6 +30,19 @@ function Test-SharePointAADB2B {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-SharePointExternalSharingDomains {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -11,16 +12,18 @@ function Test-SharePointExternalSharingDomains {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "7.2.6"
}
process {
try {
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
# Populate the auditResult object with the required properties
$params = @{
Rec = "7.2.6"
Rec = $recnum
Result = $isDomainRestrictionConfigured
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
@@ -28,6 +31,19 @@ function Test-SharePointExternalSharingDomains {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-SharePointGuestsItemSharing {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Define your parameters here
@@ -11,16 +12,18 @@ function Test-SharePointGuestsItemSharing {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "7.2.5"
}
process {
try {
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
# Populate the auditResult object with the required properties
$params = @{
Rec = "7.2.5"
Rec = $recnum
Result = $isGuestResharingPrevented
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
@@ -28,6 +31,19 @@ function Test-SharePointGuestsItemSharing {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-SpamPolicyAdminNotify {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added if needed
@@ -11,9 +12,11 @@ function Test-SpamPolicyAdminNotify {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "2.1.6"
}
process {
try {
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
# Get the default hosted outbound spam filter policy
@@ -35,7 +38,7 @@ function Test-SpamPolicyAdminNotify {
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = "2.1.6"
Rec = $recnum
Result = $areSettingsEnabled
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
@@ -43,6 +46,19 @@ function Test-SpamPolicyAdminNotify {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-TeamsExternalAccess {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
@@ -11,9 +12,11 @@ function Test-TeamsExternalAccess {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "8.2.1"
}
process {
try {
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -30,7 +33,7 @@ function Test-TeamsExternalAccess {
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = "8.2.1"
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
@@ -38,6 +41,19 @@ function Test-TeamsExternalAccess {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View File

@@ -1,5 +1,6 @@
function Test-TeamsExternalFileSharing {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added here if needed
@@ -11,9 +12,11 @@ function Test-TeamsExternalFileSharing {
# Initialization code, if needed
$auditResult = [CISAuditResult]::new()
$recnum = "8.1.1"
}
process {
try {
# 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services
# Connect to Teams PowerShell using Connect-MicrosoftTeams
@@ -34,7 +37,7 @@ function Test-TeamsExternalFileSharing {
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = "8.1.1"
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
@@ -42,6 +45,19 @@ function Test-TeamsExternalFileSharing {
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
Write-Error "An error occurred during the test: $_"
# Retrieve the description from the test definitions
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
# Call Initialize-CISAuditResult with error parameters
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
}
}
end {
# Return auditResult

View 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'
}
}
}
}

View 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'
}
}
}
}

View 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'
}
}
}
}

View 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'
}
}
}
}

View 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'
}
}
}
}

View 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'
}
}
}
}

View 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'
}
}
}
}

View 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'
}
}
}
}