32 Commits

Author SHA1 Message Date
Doug Rios
1240f74450 Merge pull request #139 from CriticalSolutionsNetwork/138-add-hash-computation-to-file-output-of-export-function-and-compress-into-archive
138 add hash computation to file output of export function and compress into archive
2024-07-01 13:22:15 -05:00
DrIOS
063124eef3 docs: Update build help/CHANGELOG 2024-07-01 13:19:44 -05:00
DrIOS
14f3889378 docs: Update Help/WikiFiles/HTML Help 2024-07-01 13:18:54 -05:00
DrIOS
3790ec00de add: Zip and hash computation to function 2024-07-01 13:12:56 -05:00
Doug Rios
c1171ddca5 Merge pull request #137 from CriticalSolutionsNetwork/132-formatting-microsoft-graph
fix: MgGraph commands formatting
2024-06-30 15:37:02 -05:00
Douglas Rios
fc7c8ec88f docs: Update CHANGELOG 2024-06-30 15:34:58 -05:00
Douglas Rios
2fc814205d fix: MgGraph commands formatting 2024-06-30 15:32:05 -05:00
Doug Rios
7309925e89 Merge pull request #136 from CriticalSolutionsNetwork/135-add-parameter-validation-to-new-parameters
135 add parameter validation to new parameters
2024-06-30 12:55:10 -05:00
DrIOS
5637855c8b docs: Update CHANGELOG 2024-06-30 12:53:21 -05:00
DrIOS
ac98307ed1 fix: parameter validation for new parameters in Invoke-M365SecurityAudit function 2024-06-30 12:53:07 -05:00
Doug Rios
a6121a1273 Merge pull request #134 from CriticalSolutionsNetwork/133-formatting-ms-teams
133 formatting ms teams
2024-06-30 12:43:43 -05:00
DrIOS
0da06288f1 docs: Update CHANGELOG 2024-06-30 12:41:15 -05:00
DrIOS
416d387c20 docs: Update WIKI and xml help 2024-06-30 12:39:44 -05:00
DrIOS
6e558ac671 docs: Update WIKI and xml help 2024-06-30 12:38:58 -05:00
DrIOS
eecda359d7 docs: Update README and HTML Help 2024-06-30 12:36:44 -05:00
DrIOS
355c12b450 fix: 8.6.1 simplified output and added object comment 2024-06-30 12:33:20 -05:00
DrIOS
9a6bda9e2e fix: 8.5.7 simplified output and added object comment 2024-06-30 12:27:01 -05:00
DrIOS
e993f186af fix: formatting 2024-06-30 12:24:36 -05:00
DrIOS
5804ca25c1 fix: 8.5.6 simplified output and added object comment 2024-06-30 12:19:49 -05:00
DrIOS
1e106f94ba fix: 8.5.5 simplified output and added object comment 2024-06-30 12:15:58 -05:00
DrIOS
80015c78d5 fix: 8.5.4 simplified output and added object comment 2024-06-30 12:10:43 -05:00
DrIOS
678c287d03 Update CHANGELOG 2024-06-30 11:47:07 -05:00
DrIOS
540fe11ce9 fix: 8.5.3 simplified output and added object comment 2024-06-30 11:46:53 -05:00
DrIOS
1d2fa9ea3a fix: 8.5.1 simplified output and added object comment 2024-06-30 11:40:26 -05:00
DrIOS
ad5ce2db7f add: remediation info and mock object to 8.5.1 2024-06-30 11:36:11 -05:00
DrIOS
97fd8127d3 add: ApprovedFederatedDomains parameter for test 8.2.1-Formatting 2024-06-30 11:34:49 -05:00
DrIOS
83177cccc2 add: ApprovedFederatedDomains parameter for test 8.2.1 2024-06-30 11:27:13 -05:00
DrIOS
bc85fa5fb8 docs: formatting 2024-06-30 10:17:58 -05:00
DrIOS
e77d786535 docs: Update CHANGELOG 2024-06-30 10:17:32 -05:00
DrIOS
20124cdbb5 fix: Add plurality to approved storage providers parameter for 8.1.1 2024-06-30 10:17:10 -05:00
DrIOS
fc9ff57576 fix: Add parameter for approved storage providers for 8.1.1 2024-06-30 10:11:25 -05:00
DrIOS
5125d7f684 fix: Corrected logic for 8.1.1 2024-06-30 09:21:41 -05:00
30 changed files with 729 additions and 397 deletions

View File

@@ -6,6 +6,35 @@ The format is based on and uses the types of changes according to [Keep a Change
### Added
- Added hash and compress steps to `Export-M365SecurityAuditTable` function.
## [0.1.21] - 2024-07-01
### Fixed
- Formatting for MgGraph tests.
## [0.1.20] - 2024-06-30
### Fixed
- Fixed parameter validation for new parameters in `Invoke-M365SecurityAudit` function
## [0.1.19] - 2024-06-30
### Added
- Added `ApprovedCloudStorageProviders` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved cloud storage providers for 8.1.1.
- Added `ApprovedFederatedDomains` parameter to `Invoke-M365SecurityAudit` to allow for testing of approved federated domains for 8.5.1.
### Fixed
- Fixed various MSTeams tests to be more accurate and include more properties in the output.
## [0.1.18] - 2024-06-29
### Added
- Added `Get-PhishPolicyDetail` and `Test-PhishPolicyCompliance` private functions to help test for phishing policy compliance.
### Fixed
@@ -16,8 +45,6 @@ The format is based on and uses the types of changes according to [Keep a Change
- Changed main function parameter for Domain to `DomainName`.
## [0.1.17] - 2024-06-28
### Fixed

BIN
README.md

Binary file not shown.

Binary file not shown.

View File

@@ -190,10 +190,11 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
## INPUTS
### [CISAuditResult[]], [string]
### [CISAuditResult[]] - An array of CISAuditResult objects.
### [string] - A path to a CSV file.
## OUTPUTS
### [PSCustomObject]
### [PSCustomObject] - A custom object containing the path to the zip file and its hash.
## NOTES
## RELATED LINKS

View File

@@ -14,47 +14,52 @@ Invokes a security audit for Microsoft 365 environments.
### Default (Default)
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-DoNotConnect] [-DoNotDisconnect]
[-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>]
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### ELevelFilter
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -ELevel <String>
-ProfileLevel <String> [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections]
[-WhatIf] [-Confirm] [<CommonParameters>]
-ProfileLevel <String> [-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>]
[-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm]
[<CommonParameters>]
```
### IG1Filter
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG1] [-DoNotConnect]
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG1]
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### IG2Filter
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG2] [-DoNotConnect]
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG2]
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### IG3Filter
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG3] [-DoNotConnect]
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG3]
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### RecFilter
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -IncludeRecommendation <String[]>
[-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm]
[<CommonParameters>]
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
```
### SkipRecFilter
```
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -SkipRecommendation <String[]>
[-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm]
[<CommonParameters>]
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
```
## DESCRIPTION
@@ -300,6 +305,36 @@ Accept pipeline input: False
Accept wildcard characters: False
```
### -ApprovedCloudStorageProviders
Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.
```yaml
Type: String[]
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: @()
Accept pipeline input: False
Accept wildcard characters: False
```
### -ApprovedFederatedDomains
Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.
```yaml
Type: String[]
Parameter Sets: (All)
Aliases:
Required: False
Position: Named
Default value: None
Accept pipeline input: False
Accept wildcard characters: False
```
### -DoNotConnect
If specified, the cmdlet will not establish a connection to Microsoft 365 services.

View File

@@ -11,24 +11,24 @@ Locale: en-US
The `M365FoundationsCISReport` module provides a set of cmdlets to audit and report on the security compliance of Microsoft 365 environments based on CIS (Center for Internet Security) benchmarks. It enables administrators to generate detailed reports, sync data with CIS Excel sheets, and perform security audits to ensure compliance.
## M365FoundationsCISReport Cmdlets
### [Export-M365SecurityAuditTable](Export-M365SecurityAuditTable.md)
### [Export-M365SecurityAuditTable](Export-M365SecurityAuditTable)
Exports M365 security audit results to a CSV file or outputs a specific test result as an object.
### [Get-AdminRoleUserLicense](Get-AdminRoleUserLicense.md)
### [Get-AdminRoleUserLicense](Get-AdminRoleUserLicense)
Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API.
### [Get-MFAStatus](Get-MFAStatus.md)
### [Get-MFAStatus](Get-MFAStatus)
Retrieves the MFA (Multi-Factor Authentication) status for Azure Active Directory users.
### [Grant-M365SecurityAuditConsent](Grant-M365SecurityAuditConsent.md)
### [Grant-M365SecurityAuditConsent](Grant-M365SecurityAuditConsent)
Grants Microsoft Graph permissions for an auditor.
### [Invoke-M365SecurityAudit](Invoke-M365SecurityAudit.md)
### [Invoke-M365SecurityAudit](Invoke-M365SecurityAudit)
Invokes a security audit for Microsoft 365 environments.
### [Remove-RowsWithEmptyCSVStatus](Remove-RowsWithEmptyCSVStatus.md)
### [Remove-RowsWithEmptyCSVStatus](Remove-RowsWithEmptyCSVStatus)
Removes rows from an Excel worksheet where the 'CSV_Status' column is empty and saves the result to a new file.
### [Sync-CISExcelAndCsvData](Sync-CISExcelAndCsvData.md)
### [Sync-CISExcelAndCsvData](Sync-CISExcelAndCsvData)
Synchronizes and updates data in an Excel worksheet with new information from a CSV file, including audit dates.

View File

@@ -4,7 +4,7 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
<#
$ver = "v0.1.16"
$ver = "v0.1.22"
git checkout main
git pull origin main
git tag -a $ver -m "Release version $ver refactor Update"
@@ -59,6 +59,5 @@ Set-Secret -Name "GitHubToken" -Vault ModuleBuildCreds
$GalleryApiToken = Get-Secret -Name "GalleryApiToken" -Vault ModuleBuildCreds -AsPlainText
$GitHubToken = Get-Secret -Name "GitHubToken" -Vault ModuleBuildCreds -AsPlainText
$GalleryApiToken
$GitHubToken

View File

@@ -513,6 +513,19 @@ function Get-CISExoOutput {
# Retrieve the necessary settings for Teams and Exchange Online
# Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal.
# Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses.
# $ReportSubmissionPolicy Mock Object
<#
$ReportSubmissionPolicy = [PSCustomObject]@{
ReportJunkToCustomizedAddress = $true
ReportNotJunkToCustomizedAddress = $true
ReportPhishToCustomizedAddress = $true
ReportJunkAddresses = @('security@example.com')
ReportNotJunkAddresses = @('security@example.com')
ReportPhishAddresses = @('security@example.com')
ReportChatMessageEnabled = $false
ReportChatMessageToCustomizedAddressEnabled = $false
}
#>
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportJunkAddresses, ReportNotJunkAddresses, ReportPhishAddresses, ReportChatMessageEnabled, ReportChatMessageToCustomizedAddressEnabled
return $ReportSubmissionPolicy
}

View File

@@ -49,6 +49,7 @@ function Get-CISMSTeamsOutput {
# Assuming that 'approvedProviders' is a list of approved cloud storage service names
# This list must be defined according to your organization's approved cloud storage services
# Add option for approved providers.
$clientConfig = Get-CsTeamsClientConfiguration
return $clientConfig
}
@@ -91,9 +92,29 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `AllowTeamsConsumer` setting is not `False`.
# - Condition B: The `AllowPublicUsers` setting is not `False`.
# - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# $externalAccessConfig Mock Object
<#
$externalAccessConfig = [PSCustomObject]@{
Identity = 'Global'
AllowedDomains = 'AllowAllKnownDomains'
BlockedDomains = @()
AllowFederatedUsers = $true
AllowPublicUsers = $true
AllowTeamsConsumer = $true
AllowTeamsConsumerInbound = $true
}
$ApprovedFederatedDomains = @('msn.com', 'google.com')
$externalAccessConfig = [PSCustomObject]@{
Identity = 'Global'
AllowedDomains = @('msn.com', 'google.com')
BlockedDomains = @()
AllowFederatedUsers = $true
AllowPublicUsers = $false
AllowTeamsConsumer = $false
AllowTeamsConsumerInbound = $true
}
#>
$externalAccessConfig = Get-CsTenantFederationConfiguration
return $externalAccessConfig
}
@@ -114,9 +135,13 @@ function Get-CISMSTeamsOutput {
# - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`.
# - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy.
# - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# $teamsMeetingPolicy Mock Object
<#
$teamsMeetingPolicy = [PSCustomObject]@{
AllowAnonymousUsersToJoinMeeting = $true
}
#>
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
return $teamsMeetingPolicy
}
@@ -137,9 +162,13 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`.
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby.
# - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# $CsTeamsMeetingPolicyAnonymous Mock Object
<#
$CsTeamsMeetingPolicyAnonymous = [PSCustomObject]@{
AllowAnonymousUsersToStartMeeting = $true
}
#>
# Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
return $CsTeamsMeetingPolicyAnonymous
@@ -161,10 +190,14 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
# - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI.
# - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# Retrieve the Teams meeting policy for lobby bypass settings
# $CsTeamsMeetingPolicyLobby Mock Object
<#
$CsTeamsMeetingPolicyLobby = [PSCustomObject]@{
AutoAdmittedUsers = "OrganizerOnly"
}
#>
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
return $CsTeamsMeetingPolicyLobby
}
@@ -185,8 +218,13 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`.
# - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`.
# - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby.
# Retrieve Teams meeting policy for PSTN users
# $CsTeamsMeetingPolicyPSTN Mock Object
<#
$CsTeamsMeetingPolicyPSTN = [PSCustomObject]@{
AllowPSTNUsersToBypassLobby = $true
}
#>
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
return $CsTeamsMeetingPolicyPSTN
}
@@ -207,8 +245,13 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`.
# - Condition B: The setting for meeting chat allows chat for anonymous users.
# - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended.
# Retrieve the Teams meeting policy for meeting chat
# $CsTeamsMeetingPolicyChat Mock Object
<#
$CsTeamsMeetingPolicyChat = [PSCustomObject]@{
MeetingChatEnabledType = "Enabled"
}
#>
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
return $CsTeamsMeetingPolicyChat
}
@@ -229,8 +272,13 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`.
# - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers".
# - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`.
# Retrieve the Teams meeting policy for presenters
# $CsTeamsMeetingPolicyPresenters Mock Object
<#
$CsTeamsMeetingPolicyPresenters = [PSCustomObject]@{
DesignatedPresenterRoleMode = "Enabled"
}
#>
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
return $CsTeamsMeetingPolicyPresenters
}
@@ -251,17 +299,27 @@ function Get-CISMSTeamsOutput {
# - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`.
# - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command.
# - Condition C: Verification using the UI indicates that external participants can give or request control.
# Retrieve Teams meeting policy for external participant control
# $CsTeamsMeetingPolicyControl Mock Object
<#
$CsTeamsMeetingPolicyControl = [PSCustomObject]@{
AllowExternalParticipantGiveRequestControl = $true
}
#>
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
return $CsTeamsMeetingPolicyControl
}
'8.6.1' {
# Test-ReportSecurityInTeams.ps1
# 8.6.1 (L1) Ensure users can report security concerns in Teams
# Retrieve the necessary settings for Teams and Exchange Online
# Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'.
# $CsTeamsMessagingPolicy Mock Object
<#
$CsTeamsMessagingPolicy = [PSCustomObject]@{
AllowSecurityEndUserReporting = $true
}
#>
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
return $CsTeamsMessagingPolicy
}

View File

@@ -42,10 +42,12 @@ function Get-CISMgOutput {
switch ($rec) {
'1.1.1' {
# 1.1.1
# Test-AdministrativeAccountCompliance
$AdminRoleAssignmentsAndUsers = Get-AdminRoleUserAndAssignment
return $AdminRoleAssignmentsAndUsers
}
'1.1.3' {
# Test-GlobalAdminsCount
# Step: Retrieve global admin role
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
# Step: Retrieve global admin members
@@ -53,6 +55,7 @@ function Get-CISMgOutput {
return $globalAdmins
}
'1.2.1' {
# Test-ManagedApprovedPublicGroups
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
return $allGroups
}
@@ -67,16 +70,19 @@ function Get-CISMgOutput {
return $domains
}
'5.1.2.3' {
# Test-RestrictTenantCreation
# Retrieve the tenant creation policy
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
return $tenantCreationPolicy
}
'5.1.8.1' {
# Test-PasswordHashSync
# Retrieve password hash sync status (Condition A and C)
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
return $passwordHashSync
}
'6.1.2' {
# Test-MailboxAuditingE3
$tenantSkus = Get-MgSubscribedSku -All
$e3SkuPartNumber = "SPE_E3"
$founde3Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e3SkuPartNumber }
@@ -89,6 +95,7 @@ function Get-CISMgOutput {
}
}
'6.1.3' {
# Test-MailboxAuditingE5
$tenantSkus = Get-MgSubscribedSku -All
$e5SkuPartNumber = "SPE_E5"
$founde5Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber }

View File

@@ -3,9 +3,12 @@ function Invoke-TestFunction {
param (
[Parameter(Mandatory = $true)]
[PSObject]$FunctionFile,
[Parameter(Mandatory = $false)]
[string]$DomainName
[string]$DomainName,
[Parameter(Mandatory = $false)]
[string[]]$ApprovedCloudStorageProviders,
[Parameter(Mandatory = $false)]
[string[]]$ApprovedFederatedDomains
)
$functionName = $FunctionFile.BaseName
@@ -16,7 +19,12 @@ function Invoke-TestFunction {
if ('DomainName' -in $functionCmd.Parameters.Keys) {
$paramList.DomainName = $DomainName
}
if ('ApprovedCloudStorageProviders' -in $functionCmd.Parameters.Keys) {
$paramList.ApprovedCloudStorageProviders = $ApprovedCloudStorageProviders
}
if ('ApprovedFederatedDomains' -in $functionCmd.Parameters.Keys) {
$paramList.ApprovedFederatedDomains = $ApprovedFederatedDomains
}
# Use splatting to pass parameters
Write-Verbose "Running $functionName..."
try {

View File

@@ -1,45 +1,50 @@
<#
.SYNOPSIS
Exports M365 security audit results to a CSV file or outputs a specific test result as an object.
Exports Microsoft 365 security audit results to CSV or Excel files and supports outputting specific test results as objects.
.DESCRIPTION
This function exports M365 security audit results from either an array of CISAuditResult objects or a CSV file.
It can export all results to a specified path or output a specific test result as an object.
The Export-M365SecurityAuditTable function exports Microsoft 365 security audit results from an array of CISAuditResult objects or a CSV file.
It can export all results to a specified path, output a specific test result as an object, and includes options for exporting results to Excel.
Additionally, it computes hashes for the exported files and includes them in the zip archive for verification purposes.
.PARAMETER AuditResults
An array of CISAuditResult objects containing the audit results.
An array of CISAuditResult objects containing the audit results. This parameter is mandatory when exporting from audit results.
.PARAMETER CsvPath
The path to a CSV file containing the audit results.
The path to a CSV file containing the audit results. This parameter is mandatory when exporting from a CSV file.
.PARAMETER OutputTestNumber
The test number to output as an object. Valid values are "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4".
The test number to output as an object. Valid values are "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4". This parameter is used to output a specific test result.
.PARAMETER ExportAllTests
Switch to export all test results.
Switch to export all test results. When specified, all test results are exported to the specified path.
.PARAMETER ExportPath
The path where the CSV files will be exported.
The path where the CSV or Excel files will be exported. This parameter is mandatory when exporting all tests.
.PARAMETER ExportOriginalTests
Switch to export the original audit results to a CSV file.
Switch to export the original audit results to a CSV file. When specified, the original test results are exported along with the processed results.
.PARAMETER ExportToExcel
Switch to export the results to an Excel file.
Switch to export the results to an Excel file. When specified, results are exported in Excel format.
.INPUTS
[CISAuditResult[]], [string]
[CISAuditResult[]] - An array of CISAuditResult objects.
[string] - A path to a CSV file.
.OUTPUTS
[PSCustomObject]
[PSCustomObject] - A custom object containing the path to the zip file and its hash.
.EXAMPLE
Export-M365SecurityAuditTable -AuditResults $object -OutputTestNumber 6.1.2
# Output object for a single test number from audit results
# Outputs the result of test number 6.1.2 from the provided audit results as an object.
.EXAMPLE
Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp"
# Export all results from audit results to the specified path
# Exports all audit results to the specified path in CSV format.
.EXAMPLE
Export-M365SecurityAuditTable -CsvPath "C:\temp\auditresultstoday1.csv" -OutputTestNumber 6.1.2
# Output object for a single test number from CSV
# Outputs the result of test number 6.1.2 from the CSV file as an object.
.EXAMPLE
Export-M365SecurityAuditTable -ExportAllTests -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp"
# Export all results from CSV to the specified path
# Exports all audit results from the CSV file to the specified path in CSV format.
.EXAMPLE
Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" -ExportOriginalTests
# Export all results from audit results to the specified path along with the original tests
# Exports all audit results along with the original test results to the specified path in CSV format.
.EXAMPLE
Export-M365SecurityAuditTable -ExportAllTests -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" -ExportOriginalTests
# Export all results from CSV to the specified path along with the original tests
# Exports all audit results from the CSV file along with the original test results to the specified path in CSV format.
.EXAMPLE
Export-M365SecurityAuditTable -ExportAllTests -AuditResults $object -ExportPath "C:\temp" -ExportToExcel
# Exports all audit results to the specified path in Excel format.
.LINK
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Export-M365SecurityAuditTable
#>
@@ -50,33 +55,29 @@ function Export-M365SecurityAuditTable {
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromAuditResults")]
[Parameter(Mandatory = $true, Position = 2, ParameterSetName = "OutputObjectFromAuditResultsSingle")]
[CISAuditResult[]]$AuditResults,
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromCsv")]
[Parameter(Mandatory = $true, Position = 2, ParameterSetName = "OutputObjectFromCsvSingle")]
[ValidateScript({ (Test-Path $_) -and ((Get-Item $_).PSIsContainer -eq $false) })]
[string]$CsvPath,
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "OutputObjectFromAuditResultsSingle")]
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "OutputObjectFromCsvSingle")]
[ValidateSet("1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4")]
[string]$OutputTestNumber,
[Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromAuditResults")]
[Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromCsv")]
[switch]$ExportAllTests,
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")]
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")]
[string]$ExportPath,
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")]
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")]
[switch]$ExportOriginalTests,
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
[switch]$ExportToExcel
)
Begin {
$createdFiles = @() # Initialize an array to keep track of created files
if ($ExportToExcel) {
Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion "7.8.9"
}
@@ -92,22 +93,19 @@ function Export-M365SecurityAuditTable {
Initialize-CISAuditResult @params
}
}
if ($ExportAllTests) {
$TestNumbers = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
}
$results = @()
$testsToProcess = if ($OutputTestNumber) { @($OutputTestNumber) } else { $TestNumbers }
}
Process {
foreach ($test in $testsToProcess) {
$auditResult = $AuditResults | Where-Object { $_.Rec -eq $test }
if (-not $auditResult) {
Write-Information "No audit results found for the test number $test."
continue
}
switch ($test) {
"6.1.2" {
$details = $auditResult.Details
@@ -117,7 +115,6 @@ function Export-M365SecurityAuditTable {
else {
$csv = $null
}
if ($null -ne $csv) {
foreach ($row in $csv) {
$row.AdminActionsMissing = (Get-Action -AbbreviatedActions $row.AdminActionsMissing.Split(',') -ReverseActionType Admin | Where-Object { $_ -notin @("MailItemsAccessed", "Send") }) -join ','
@@ -139,7 +136,6 @@ function Export-M365SecurityAuditTable {
else {
$csv = $null
}
if ($null -ne $csv) {
foreach ($row in $csv) {
$row.AdminActionsMissing = (Get-Action -AbbreviatedActions $row.AdminActionsMissing.Split(',') -ReverseActionType Admin) -join ','
@@ -160,11 +156,11 @@ function Export-M365SecurityAuditTable {
}
}
}
}
End {
if ($ExportPath) {
$timestamp = (Get-Date).ToString("yyyy.MM.dd_HH.mm.ss")
$exportedTests = @()
foreach ($result in $results) {
$testDef = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $result.TestNumber }
if ($testDef) {
@@ -177,9 +173,11 @@ function Export-M365SecurityAuditTable {
if ($ExportToExcel) {
$xlsxPath = [System.IO.Path]::ChangeExtension($fileName, '.xlsx')
$result.Details | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2
$createdFiles += $xlsxPath # Add the created file to the array
}
else {
$result.Details | Export-Csv -Path $fileName -NoTypeInformation
$createdFiles += $fileName # Add the created file to the array
}
$exportedTests += $result.TestNumber
}
@@ -197,23 +195,52 @@ function Export-M365SecurityAuditTable {
Write-Information "No specified tests were included in the export." -InformationAction Continue
}
}
if ($ExportOriginalTests) {
# Define the test numbers to check
$TestNumbersToCheck = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
# Check for large details and update the AuditResults array
$updatedAuditResults = Get-ExceededLengthResultDetail -AuditResults $AuditResults -TestNumbersToCheck $TestNumbersToCheck -ExportedTests $exportedTests -DetailsLengthLimit 30000 -PreviewLineCount 25
$originalFileName = "$ExportPath\$timestamp`_M365FoundationsAudit.csv"
if ($ExportToExcel) {
$xlsxPath = [System.IO.Path]::ChangeExtension($originalFileName, '.xlsx')
$updatedAuditResults | Export-Excel -Path $xlsxPath -WorksheetName Table -TableName Table -AutoSize -TableStyle Medium2
$createdFiles += $xlsxPath # Add the created file to the array
}
else {
$updatedAuditResults | Export-Csv -Path $originalFileName -NoTypeInformation
$createdFiles += $originalFileName # Add the created file to the array
}
}
# Hash each file and add it to a dictionary
# Hash each file and save the hashes to a text file
$hashFilePath = "$ExportPath\$timestamp`_Hashes.txt"
$fileHashes = @()
foreach ($file in $createdFiles) {
$hash = Get-FileHash -Path $file -Algorithm SHA256
$fileHashes += "$($file): $($hash.Hash)"
}
$fileHashes | Set-Content -Path $hashFilePath
$createdFiles += $hashFilePath # Add the hash file to the array
# Create a zip file and add all the created files
$zipFilePath = "$ExportPath\$timestamp`_M365FoundationsAudit.zip"
Compress-Archive -Path $createdFiles -DestinationPath $zipFilePath
# Remove the original files after they have been added to the zip
foreach ($file in $createdFiles) {
Remove-Item -Path $file -Force
}
# Compute the hash for the zip file and rename it
$zipHash = Get-FileHash -Path $zipFilePath -Algorithm SHA256
$newZipFilePath = "$ExportPath\$timestamp`_M365FoundationsAudit_$($zipHash.Hash.Substring(0, 8)).zip"
Rename-Item -Path $zipFilePath -NewName $newZipFilePath
# Output the zip file path with hash
[PSCustomObject]@{
ZipFilePath = $newZipFilePath
}
} # End of ExportPath
elseif ($OutputTestNumber) {
if ($results[0].Details) {
return $results[0].Details
@@ -225,4 +252,9 @@ function Export-M365SecurityAuditTable {
else {
Write-Error "No valid operation specified. Please provide valid parameters."
}
# Output the created files at the end
#if ($createdFiles.Count -gt 0) {
########### $createdFiles
#}
}
}

View File

@@ -21,6 +21,10 @@
Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers.
.PARAMETER SkipRecommendation
Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers.
.PARAMETER ApprovedCloudStorageProviders
Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.
.PARAMETER ApprovedFederatedDomains
Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.
.PARAMETER DoNotConnect
If specified, the cmdlet will not establish a connection to Microsoft 365 services.
.PARAMETER DoNotDisconnect
@@ -128,32 +132,25 @@ function Invoke-M365SecurityAudit {
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
[string]$TenantAdminUrl,
[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'.")]
[Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration and DKIM Config for tests '1.3.1' and 2.1.9. The domain name of your organization, e.g., 'example.com'.")]
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
[string]$DomainName,
# E-Level with optional ProfileLevel selection
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = "Specifies the E-Level (E3 or E5) for the audit.")]
[ValidateSet('E3', 'E5')]
[string]$ELevel,
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter', HelpMessage = "Specifies the profile level (L1 or L2) for the audit.")]
[ValidateSet('L1', 'L2')]
[string]$ProfileLevel,
# IG Filters, one at a time
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')]
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter', HelpMessage = "Includes tests where IG1 is true.")]
[switch]$IncludeIG1,
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')]
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter', HelpMessage = "Includes tests where IG2 is true.")]
[switch]$IncludeIG2,
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')]
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter', HelpMessage = "Includes tests where IG3 is true.")]
[switch]$IncludeIG3,
# Inclusion of specific recommendation numbers
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter', HelpMessage = "Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers.")]
[ValidateSet(
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
@@ -164,9 +161,8 @@ function Invoke-M365SecurityAudit {
'8.5.7', '8.6.1'
)]
[string[]]$IncludeRecommendation,
# Exclusion of specific recommendation numbers
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter', HelpMessage = "Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers.")]
[ValidateSet(
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
@@ -177,24 +173,32 @@ function Invoke-M365SecurityAudit {
'8.5.7', '8.6.1'
)]
[string[]]$SkipRecommendation,
# Common parameters for all parameter sets
[Parameter(Mandatory = $false, HelpMessage = "Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.")]
[ValidateSet(
'GoogleDrive', 'ShareFile', 'Box', 'DropBox', 'Egnyte'
)]
[string[]]$ApprovedCloudStorageProviders = @(),
[Parameter(Mandatory = $false, HelpMessage = "Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.")]
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
[string[]]$ApprovedFederatedDomains,
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not establish a connection to Microsoft 365 services.")]
[switch]$DoNotConnect,
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not disconnect from Microsoft 365 services after execution.")]
[switch]$DoNotDisconnect,
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not check for the presence of required modules.")]
[switch]$NoModuleCheck,
[Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.")]
[switch]$DoNotConfirmConnections
)
Begin {
if ($script:MaximumFunctionCount -lt 8192) {
$script:MaximumFunctionCount = 8192
}
# Ensure required modules are installed
$requiredModules = Get-RequiredModule -AuditFunction
# Format the required modules list
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
# Check and install required modules if necessary
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules: $requiredModulesFormatted", "Check")) {
Write-Host "Checking for and installing required modules..." -ForegroundColor DarkMagenta
@@ -202,13 +206,11 @@ function Invoke-M365SecurityAudit {
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
}
}
# Load test definitions from CSV
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath
# Load the Test Definitions into the script scope for use in other functions
$script:TestDefinitionsObject = $testDefinitions
# Apply filters based on parameter sets
$params = @{
TestDefinitions = $testDefinitions
@@ -237,17 +239,14 @@ function Invoke-M365SecurityAudit {
# 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
# 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
# Establishing connections if required
try {
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
@@ -260,8 +259,6 @@ function Invoke-M365SecurityAudit {
Write-Host "Connection execution aborted: $_" -ForegroundColor Red
break
}
try {
Write-Host "A total of $($totalTests) tests were selected to run..." -ForegroundColor DarkMagenta
# Import the test functions
@@ -278,7 +275,6 @@ function Invoke-M365SecurityAudit {
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
}
}
$currentTestIndex = 0
# Execute each test function from the prepared list
foreach ($testFunction in $testFiles) {
@@ -286,7 +282,7 @@ function Invoke-M365SecurityAudit {
Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
$functionName = $testFunction.BaseName
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders -ApprovedFederatedDomains $ApprovedFederatedDomains
# Add the result to the collection
[void]$allAuditResults.Add($auditResult)
}
@@ -311,7 +307,6 @@ function Invoke-M365SecurityAudit {
# Return all collected audit results
# Define the test numbers to check
$TestNumbersToCheck = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
# Check for large details in the audit results
$exceedingTests = Get-ExceededLengthResultDetail -AuditResults $allAuditResults -TestNumbersToCheck $TestNumbersToCheck -ReturnExceedingTestsOnly -DetailsLengthLimit 30000
if ($exceedingTests.Count -gt 0) {
@@ -323,6 +318,3 @@ function Invoke-M365SecurityAudit {
}
}
}

View File

@@ -277,7 +277,15 @@
<command:inputTypes>
<command:inputType>
<dev:type>
<maml:name>[CISAuditResult[]], [string]</maml:name>
<maml:name>[CISAuditResult[]] - An array of CISAuditResult objects.</maml:name>
</dev:type>
<maml:description>
<maml:para></maml:para>
</maml:description>
</command:inputType>
<command:inputType>
<dev:type>
<maml:name>[string] - A path to a CSV file.</maml:name>
</dev:type>
<maml:description>
<maml:para></maml:para>
@@ -287,7 +295,7 @@
<command:returnValues>
<command:returnValue>
<dev:type>
<maml:name>[PSCustomObject]</maml:name>
<maml:name>[PSCustomObject] - A custom object containing the path to the zip file and its hash.</maml:name>
</dev:type>
<maml:description>
<maml:para></maml:para>
@@ -908,6 +916,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>
@@ -1012,6 +1044,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>
@@ -1116,6 +1172,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>
@@ -1220,6 +1300,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>False</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>
@@ -1325,6 +1429,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>
@@ -1430,6 +1558,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>
@@ -1607,6 +1759,30 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedCloudStorageProviders</maml:name>
<maml:description>
<maml:para>Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>@()</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>ApprovedFederatedDomains</maml:name>
<maml:description>
<maml:para>Specifies the approved federated domains for the audit test 8.2.1. Accepts an array of allowed domain names.</maml:para>
</maml:description>
<command:parameterValue required="true" variableLength="false">String[]</command:parameterValue>
<dev:type>
<maml:name>String[]</maml:name>
<maml:uri />
</dev:type>
<dev:defaultValue>None</dev:defaultValue>
</command:parameter>
<command:parameter required="false" variableLength="true" globbing="false" pipelineInput="False" position="named" aliases="none">
<maml:name>DoNotConnect</maml:name>
<maml:description>

View File

@@ -1,7 +1,6 @@
function Test-AdministrativeAccountCompliance {
[CmdletBinding()]
param ()
begin {
# The following conditions are checked:
# Condition A: The administrative account is cloud-only (not synced).
@@ -11,16 +10,12 @@ function Test-AdministrativeAccountCompliance {
$recnum = "1.1.1"
Write-Verbose "Starting Test-AdministrativeAccountCompliance with Rec: $recnum"
}
process {
try {
# Retrieve admin roles, assignments, and user details including licenses
Write-Verbose "Retrieving admin roles, assignments, and user details including licenses"
$adminRoleAssignments = Get-CISMgOutput -Rec $recnum
$adminRoleUsers = @()
foreach ($roleName in $adminRoleAssignments.Keys) {
$assignments = $adminRoleAssignments[$roleName]
foreach ($assignment in $assignments) {
@@ -29,21 +24,16 @@ function Test-AdministrativeAccountCompliance {
$userPrincipalName = $userDetails.UserPrincipalName
$licenses = $assignment.Licenses
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
# Condition A: Check if the account is cloud-only
$cloudOnlyStatus = if ($userDetails.OnPremisesSyncEnabled) { "Fail" } else { "Pass" }
# Condition B: Check if the account has valid licenses
$hasValidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -contains $_ }
$validLicensesStatus = if ($hasValidLicense) { "Pass" } else { "Fail" }
# Condition C: Check if the account has no other licenses
$hasInvalidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -notcontains $_ }
$invalidLicenses = $licenses.SkuPartNumber | Where-Object { $validLicenses -notcontains $_ }
$applicationAssignmentStatus = if ($hasInvalidLicense) { "Fail" } else { "Pass" }
Write-Verbose "User: $userPrincipalName, Cloud-Only: $cloudOnlyStatus, Valid Licenses: $validLicensesStatus, Invalid Licenses: $($invalidLicenses -join ', ')"
# Collect user information
$adminRoleUsers += [PSCustomObject]@{
UserName = $userPrincipalName
@@ -57,17 +47,14 @@ function Test-AdministrativeAccountCompliance {
}
}
}
# Group admin role users by UserName and collect unique roles and licenses
Write-Verbose "Grouping admin role users by UserName"
$uniqueAdminRoleUsers = $adminRoleUsers | Group-Object -Property UserName | ForEach-Object {
$first = $_.Group | Select-Object -First 1
$roles = ($_.Group.RoleName -join ', ')
$licenses = (($_.Group | Select-Object -ExpandProperty Licenses) -join ',').Split(',') | Select-Object -Unique
$first | Select-Object UserName, UserId, HybridUser, @{Name = 'Roles'; Expression = { $roles } }, @{Name = 'Licenses'; Expression = { $licenses -join '|' } }, CloudOnlyStatus, ValidLicensesStatus, ApplicationAssignmentStatus
}
# Identify non-compliant users based on conditions A, B, and C
Write-Verbose "Identifying non-compliant users based on conditions"
$nonCompliantUsers = $uniqueAdminRoleUsers | Where-Object {
@@ -75,7 +62,6 @@ function Test-AdministrativeAccountCompliance {
$_.ValidLicensesStatus -eq "Fail" -or # Fails Condition B
$_.ApplicationAssignmentStatus -eq "Fail" # Fails Condition C
}
# Generate failure reasons
Write-Verbose "Generating failure reasons for non-compliant users"
$failureReasons = $nonCompliantUsers | ForEach-Object {
@@ -88,13 +74,10 @@ function Test-AdministrativeAccountCompliance {
else {
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
}
$result = $nonCompliantUsers.Count -eq 0
$status = if ($result) { 'Pass' } else { 'Fail' }
$details = if ($nonCompliantUsers) { "Username | Roles | Cloud-Only Status | EntraID P1/P2 License Status | Other Applications Assigned Status`n$failureReasons" } else { "N/A" }
Write-Verbose "Assessment completed. Result: $status"
# Create the parameter splat
$params = @{
Rec = $recnum
@@ -103,7 +86,6 @@ function Test-AdministrativeAccountCompliance {
Details = $details
FailureReason = $failureReason
}
$auditResult = Initialize-CISAuditResult @params
}
catch {
@@ -111,7 +93,6 @@ function Test-AdministrativeAccountCompliance {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Output the result
return $auditResult

View File

@@ -5,16 +5,14 @@ function Test-BlockChannelEmails {
# Aligned
# Parameters can be added here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.1.2"
Write-Verbose "Running Test-BlockChannelEmails for $recnum..."
}
process {
try {
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
#
@@ -31,11 +29,9 @@ function Test-BlockChannelEmails {
# - Condition A: The `AllowEmailIntoChannel` setting in Teams is not set to `False`.
# - Condition B: The setting `Users can send emails to a channel email address` is not set to `Off` in the Teams admin center.
# - Condition C: Verification using PowerShell indicates that the `AllowEmailIntoChannel` setting is enabled.
# Retrieve Teams client configuration
$teamsClientConfig = Get-CISMSTeamsOutput -Rec $recnum
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
# Prepare failure reasons and details based on compliance
$failureReasons = if ($allowEmailIntoChannel) {
"Emails can be sent to a channel email address" # Condition A Fail: AllowEmailIntoChannel is True
@@ -43,14 +39,12 @@ function Test-BlockChannelEmails {
else {
"N/A" # Condition A Pass: AllowEmailIntoChannel is False
}
$details = if ($allowEmailIntoChannel) {
"AllowEmailIntoChannel is set to True" # Condition B Fail: Emails are allowed
}
else {
"AllowEmailIntoChannel is set to False" # Condition B Pass: Emails are blocked
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -66,7 +60,6 @@ function Test-BlockChannelEmails {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,16 +5,14 @@ function Test-DialInBypassLobby {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.4"
Write-Verbose "Running Test-DialInBypassLobby for $recnum..."
}
process {
try {
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
#
@@ -31,11 +29,15 @@ function Test-DialInBypassLobby {
# - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`.
# - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`.
# - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby.
# Retrieve Teams meeting policy for PSTN users
# $CsTeamsMeetingPolicyPSTN Mock Object
<#
$CsTeamsMeetingPolicyPSTN = [PSCustomObject]@{
AllowPSTNUsersToBypassLobby = $true
}
#>
$CsTeamsMeetingPolicyPSTN = Get-CISMSTeamsOutput -Rec $recnum
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $PSTNBypassDisabled) {
"Users dialing in can bypass the lobby"
@@ -43,14 +45,12 @@ function Test-DialInBypassLobby {
else {
"N/A"
}
$details = if ($PSTNBypassDisabled) {
"AllowPSTNUsersToBypassLobby is set to False"
}
else {
"AllowPSTNUsersToBypassLobby is set to True"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -66,7 +66,6 @@ function Test-DialInBypassLobby {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,17 +5,14 @@ function Test-ExternalNoControl {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.7"
Write-Verbose "Running Test-ExternalNoControl for $recnum..."
}
process {
try {
# 8.5.7 (L1) Ensure external participants can't give or request control
#
@@ -32,12 +29,16 @@ function Test-ExternalNoControl {
# - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`.
# - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command.
# - Condition C: Verification using the UI indicates that external participants can give or request control.
# Retrieve Teams meeting policy for external participant control
# $CsTeamsMeetingPolicyControl Mock Object
<#
$CsTeamsMeetingPolicyControl = [PSCustomObject]@{
AllowExternalParticipantGiveRequestControl = $true
}
#>
$CsTeamsMeetingPolicyControl = Get-CISMSTeamsOutput -Rec $recnum
# Check if external participants can give or request control
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $externalControlRestricted) {
"External participants can give or request control"
@@ -45,14 +46,12 @@ function Test-ExternalNoControl {
else {
"N/A"
}
$details = if ($externalControlRestricted) {
"AllowExternalParticipantGiveRequestControl is set to False"
}
else {
"AllowExternalParticipantGiveRequestControl is set to True"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -68,7 +67,6 @@ function Test-ExternalNoControl {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -4,7 +4,6 @@ function Test-GlobalAdminsCount {
param (
# Define your parameters here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
@@ -23,23 +22,19 @@ function Test-GlobalAdminsCount {
# - Condition A: The number of global admins is less than 2.
# - Condition B: The number of global admins is more than 4.
# - Condition C: Any discrepancies or errors in retrieving the list of global admin usernames.
# Initialization code, if needed
$recnum = "1.1.3"
Write-Verbose "Starting Test-GlobalAdminsCount with Rec: $recnum"
}
process {
try {
$globalAdmins = Get-CISMgOutput -Rec $recnum
# Step: Count the number of global admins
$globalAdminCount = $globalAdmins.Count
# Step: Retrieve and format the usernames of global admins
$globalAdminUsernames = ($globalAdmins | ForEach-Object {
"$($_.AdditionalProperties["displayName"]) ($($_.AdditionalProperties["userPrincipalName"]))"
}) -join ', '
# Step: Determine failure reasons based on global admin count
$failureReasons = if ($globalAdminCount -lt 2) {
"Less than 2 global admins: $globalAdminUsernames"
@@ -50,10 +45,8 @@ function Test-GlobalAdminsCount {
else {
"N/A"
}
# Step: Prepare details for the audit result
$details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -69,7 +62,6 @@ function Test-GlobalAdminsCount {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -4,14 +4,12 @@ function Test-ManagedApprovedPublicGroups {
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "1.2.1"
Write-Verbose "Starting Test-ManagedApprovedPublicGroups with Rec: $recnum"
# Conditions for 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
#
# Validate test for a pass:
@@ -26,12 +24,10 @@ function Test-ManagedApprovedPublicGroups {
# - Condition A: One or more groups have the status 'Public' in the privacy column on the Active teams and groups page.
# - Condition B: Using Microsoft Graph PowerShell, one or more groups return a status of 'Public' when checked.
}
process {
try {
# Step: Retrieve all groups with visibility set to 'Public'
$allGroups = Get-CISMgOutput -Rec $recnum
# Step: Determine failure reasons based on the presence of public groups
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {
"There are public groups present that are not organizationally managed/approved."
@@ -39,7 +35,6 @@ function Test-ManagedApprovedPublicGroups {
else {
"N/A"
}
# Step: Prepare details for the audit result
$details = if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
"No public groups found."
@@ -48,7 +43,6 @@ function Test-ManagedApprovedPublicGroups {
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
"Public groups found: $($groupDetails -join ', ')"
}
# Step: Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -64,7 +58,6 @@ function Test-ManagedApprovedPublicGroups {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,14 +5,13 @@ function Test-MeetingChatNoAnonymous {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.5"
Write-Verbose "Running Test-MeetingChatNoAnonymous for $recnum..."
}
process {
try {
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
@@ -30,22 +29,24 @@ function Test-MeetingChatNoAnonymous {
# - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`.
# - Condition B: The setting for meeting chat allows chat for anonymous users.
# - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended.
# Retrieve the Teams meeting policy for meeting chat
# $CsTeamsMeetingPolicyChat Mock Object
<#
$CsTeamsMeetingPolicyChat = [PSCustomObject]@{
MeetingChatEnabledType = "Enabled"
}
#>
$CsTeamsMeetingPolicyChat = Get-CISMSTeamsOutput -Rec $recnum
# Condition A: Check if the MeetingChatEnabledType is set to 'EnabledExceptAnonymous'
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
# Prepare failure reasons and details based on compliance
$failureReasons = if ($chatAnonDisabled) {
"N/A"
}
else {
"Meeting chat allows anonymous users"
"Meeting chat allows anonymous users. User the following command to remediate:`nSet-CsTeamsMeetingPolicy -Identity Global -MeetingChatEnabledType `"EnabledExceptAnonymous`""
}
$details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -61,7 +62,6 @@ function Test-MeetingChatNoAnonymous {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,14 +5,13 @@ function Test-NoAnonymousMeetingJoin {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.1"
Write-Verbose "Running Test-NoAnonymousMeetingJoin for $recnum..."
}
process {
try {
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
@@ -30,22 +29,23 @@ function Test-NoAnonymousMeetingJoin {
# - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`.
# - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy.
# - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# $teamsMeetingPolicy Mock Object
<#
$teamsMeetingPolicy = [PSCustomObject]@{
AllowAnonymousUsersToJoinMeeting = $true
}
#>
$teamsMeetingPolicy = Get-CISMSTeamsOutput -Rec $recnum
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
# Prepare failure reasons and details based on compliance
$failureReasons = if ($allowAnonymousUsersToJoinMeeting) {
"Anonymous users are allowed to join meetings"
"Anonymous users are allowed to join meetings, remediate with the following command if needed:`nSet-CsTeamsMeetingPolicy -Identity Global -AllowAnonymousUsersToJoinMeeting `$false"
}
else {
"N/A"
}
$details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -61,7 +61,6 @@ function Test-NoAnonymousMeetingJoin {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,14 +5,13 @@ function Test-NoAnonymousMeetingStart {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.2"
Write-Verbose "Running Test-NoAnonymousMeetingStart for $recnum..."
}
process {
try {
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
@@ -30,13 +29,10 @@ function Test-NoAnonymousMeetingStart {
# - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`.
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby.
# - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings
$CsTeamsMeetingPolicyAnonymous = Get-CISMSTeamsOutput -Rec $recnum
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
# Prepare failure reasons and details based on compliance
$failureReasons = if ($anonymousStartDisabled) {
"N/A"
@@ -44,9 +40,7 @@ function Test-NoAnonymousMeetingStart {
else {
"Anonymous users and dial-in callers can start a meeting" # Condition A and B
}
$details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)" # Condition C
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -62,7 +56,6 @@ function Test-NoAnonymousMeetingStart {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,14 +5,13 @@ function Test-OrgOnlyBypassLobby {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.3"
Write-Verbose "Running Test-OrgOnlyBypassLobby for $recnum..."
}
process {
try {
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
@@ -30,28 +29,23 @@ function Test-OrgOnlyBypassLobby {
# - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
# - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI.
# - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# Retrieve the Teams meeting policy for lobby bypass settings
$CsTeamsMeetingPolicyLobby = Get-CISMSTeamsOutput -Rec $recnum
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $lobbyBypassRestricted) {
# Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
"AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
}else {
"N/A"
}
$details = if ($lobbyBypassRestricted) {
# Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
"Only people in the organization can bypass the lobby."
}else {
# Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
"External participants can bypass the lobby"
"AutoAdmittedUsers is not set to EveryoneInCompanyExcludingGuests"
}
# Create and populate the CISAuditResult object
$params = @{
@@ -68,7 +62,6 @@ function Test-OrgOnlyBypassLobby {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,14 +5,13 @@ function Test-OrganizersPresent {
# Aligned
# Parameters can be defined here if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.5.6"
Write-Verbose "Running Test-OrganizersPresent for $recnum..."
}
process {
try {
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
@@ -30,26 +29,28 @@ function Test-OrganizersPresent {
# - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`.
# - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers".
# - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`.
# Retrieve the Teams meeting policy for presenters
# $CsTeamsMeetingPolicyPresenters Mock Object
<#
$CsTeamsMeetingPolicyPresenters = [PSCustomObject]@{
DesignatedPresenterRoleMode = "Enabled"
}
#>
$CsTeamsMeetingPolicyPresenters = Get-CISMSTeamsOutput -Rec $recnum
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $presenterRoleRestricted) {
"Others besides organizers and co-organizers can present"
"Others besides organizers and co-organizers can present. Use the following command to remediate:`nSet-CsTeamsMeetingPolicy -Identity Global -DesignatedPresenterRoleMode `"OrganizerOnlyUserOverride`""
}
else {
"N/A"
}
$details = if ($presenterRoleRestricted) {
"Only organizers and co-organizers can present."
}
else {
"DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
}
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -65,7 +66,6 @@ function Test-OrganizersPresent {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -5,7 +5,6 @@ function Test-PasswordHashSync {
# Aligned
# Parameters can be added if needed
)
begin {
# Conditions for 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
#
@@ -22,21 +21,18 @@ function Test-PasswordHashSync {
# - Condition A: Password hash sync is not enabled in the Azure AD Connect tool on the on-premises server.
# - Condition B: Password hash sync is not verified as enabled in the Azure AD Connect Sync section in the Microsoft Entra admin center.
# - Condition C: Using Microsoft Graph PowerShell, the verification command returns no result indicating that password sync is not enabled for the on-premises AD.
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "5.1.8.1"
Write-Verbose "Starting Test-PasswordHashSync with Rec: $recnum"
}
process {
try {
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
# Retrieve password hash sync status (Condition A and C)
$passwordHashSync = Get-CISMgOutput -Rec $recnum
$hashSyncResult = $passwordHashSync
# Prepare failure reasons and details based on compliance
$failureReasons = if (-not $hashSyncResult) {
"Password hash sync for hybrid deployments is not enabled"
@@ -44,9 +40,7 @@ function Test-PasswordHashSync {
else {
"N/A"
}
$details = "OnPremisesSyncEnabled: $($passwordHashSync)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -62,7 +56,6 @@ function Test-PasswordHashSync {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -18,9 +18,28 @@ function Test-ReportSecurityInTeams {
# 8.6.1 (L1) Ensure users can report security concerns in Teams
# Retrieve the necessary settings for Teams and Exchange Online
# Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'.
# $CsTeamsMessagingPolicy Mock Object
<#
$CsTeamsMessagingPolicy = [PSCustomObject]@{
AllowSecurityEndUserReporting = $true
}
#>
$CsTeamsMessagingPolicy = Get-CISMSTeamsOutput -Rec $recnum
# Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal.
# Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses.
# $ReportSubmissionPolicy Mock Object
<#
$ReportSubmissionPolicy = [PSCustomObject]@{
ReportJunkToCustomizedAddress = $true
ReportNotJunkToCustomizedAddress = $true
ReportPhishToCustomizedAddress = $true
ReportJunkAddresses = @('security@example.com')
ReportNotJunkAddresses = @('security@example.com')
ReportPhishAddresses = @('security@example.com')
ReportChatMessageEnabled = $false
ReportChatMessageToCustomizedAddressEnabled = $false
}
#>
$ReportSubmissionPolicy = Get-CISExoOutput -Rec $recnum
# Check if all the required settings are enabled
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and

View File

@@ -5,23 +5,19 @@ function Test-RestrictTenantCreation {
# Aligned
# Parameters can be added if needed
)
begin {
# Dot source the class script if necessary
#. .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "5.1.2.3"
Write-Verbose "Starting Test-RestrictTenantCreation with Rec: $recnum"
<#
Conditions for 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
Validate test for a pass:
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
- Specific conditions to check:
- Condition A: Restrict non-admin users from creating tenants is set to 'Yes' in the Azure AD and Entra administration portal.
- Condition B: Using PowerShell, the setting for restricting non-admin users from creating tenants is set to 'Yes'.
Validate test for a fail:
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
- Specific conditions to check:
@@ -29,15 +25,12 @@ function Test-RestrictTenantCreation {
- Condition B: Using PowerShell, the setting for restricting non-admin users from creating tenants is not set to 'Yes'.
#>
}
process {
try {
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
# Retrieve the tenant creation policy
$tenantCreationPolicy = Get-CISMgOutput -Rec $recnum
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
# Prepare failure reasons and details based on compliance
$failureReasons = if ($tenantCreationResult) {
"N/A"
@@ -45,9 +38,7 @@ function Test-RestrictTenantCreation {
else {
"Non-admin users can create tenants"
}
$details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
# Create and populate the CISAuditResult object
$params = @{
Rec = $recnum
@@ -63,7 +54,6 @@ function Test-RestrictTenantCreation {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return the audit result
return $auditResult

View File

@@ -2,17 +2,16 @@ function Test-TeamsExternalAccess {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be defined here if needed
[Parameter(Mandatory = $false, HelpMessage = "Specifies the approved federated domains for the audit. Accepts an array of allowed domain names.")]
[string[]]$ApprovedFederatedDomains
)
begin {
# Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.2.1"
Write-Verbose "Running Test-TeamsExternalAccess for $recnum..."
}
process {
try {
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
@@ -23,33 +22,60 @@ function Test-TeamsExternalAccess {
# - Condition A: The `AllowTeamsConsumer` setting is `False`.
# - Condition B: The `AllowPublicUsers` setting is `False`.
# - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names.
#
# Validate test for a fail:
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
# - Specific conditions to check:
# - Condition A: The `AllowTeamsConsumer` setting is not `False`.
# - Condition B: The `AllowPublicUsers` setting is not `False`.
# - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$externalAccessConfig = Get-CISMSTeamsOutput -Rec $recnum
$allowedDomainsLimited = $false
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
$allowedDomainsLimited = $true
# $externalAccessConfig Mock Object
<#
$externalAccessConfig = [PSCustomObject]@{
Identity = 'Global'
AllowedDomains = 'AllowAllKnownDomains'
BlockedDomains = @()
AllowFederatedUsers = $true
AllowPublicUsers = $true
AllowTeamsConsumer = $true
AllowTeamsConsumerInbound = $true
}
$ApprovedFederatedDomains = @('msn.com', 'google.com')
$externalAccessConfig = [PSCustomObject]@{
Identity = 'Global'
AllowedDomains = @('msn.com', 'google.com')
BlockedDomains = @()
AllowFederatedUsers = $true
AllowPublicUsers = $false
AllowTeamsConsumer = $false
AllowTeamsConsumerInbound = $true
}
#>
$externalAccessConfig = Get-CISMSTeamsOutput -Rec $recnum
# Testing
#$externalAccessConfig.AllowedDomains = @("msn.com", "google.com")
#$externalAccessConfig.AllowTeamsConsumer = $false
#$externalAccessConfig.AllowPublicUsers = $false
#$externalAccessConfig.AllowFederatedUsers = $true
# The above is for testing and will be replaced with the actual values from the Teams PowerShell output in production.
$allowedDomainsLimited = $false
$allowedDomainsMatch = $false
$invalidDomains = @()
if ($externalAccessConfig.AllowFederatedUsers) {
if ($externalAccessConfig.AllowedDomains -ne 'AllowAllKnownDomains' -and $externalAccessConfig.AllowedDomains.Count -gt 0) {
$allowedDomainsLimited = $true
if ($ApprovedFederatedDomains) {
$invalidDomains = $externalAccessConfig.AllowedDomains | Where-Object { $_ -notin $ApprovedFederatedDomains }
if ($invalidDomains.Count -eq 0) {
$invalidDomains = "None"
}
$allowedDomainsMatch = $invalidDomains.Count -eq 0
}
}
}
# Check if the configurations are as recommended
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or ($allowedDomainsLimited -and $allowedDomainsMatch))
# Create an instance of CISAuditResult and populate it
$params = @{
Rec = $recnum
Result = $isCompliant
Status = if ($isCompliant) { "Pass" } else { "Fail" }
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited; AllowedDomains match: $allowedDomainsMatch; Invalid Domains: $($invalidDomains -join ', ')"
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant. Invalid domains found: $($invalidDomains -join ', ')" } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
}
@@ -58,7 +84,6 @@ function Test-TeamsExternalAccess {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return auditResult
return $auditResult

View File

@@ -2,48 +2,65 @@ function Test-TeamsExternalFileSharing {
[CmdletBinding()]
[OutputType([CISAuditResult])]
param (
# Aligned
# Parameters can be added here if needed
[Parameter(Mandatory = $false)]
[string[]]$ApprovedCloudStorageProviders
)
begin {
# Dot source the class script if necessary
# . .\source\Classes\CISAuditResult.ps1
# Initialization code, if needed
$recnum = "8.1.1"
Write-Verbose "Running Test-TeamsExternalFileSharing for $recnum..."
}
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
# Condition A: The `AllowDropbox` setting is set to `False`.
# Condition B: The `AllowBox` setting is set to `False`.
# Condition C: The `AllowGoogleDrive` setting is set to `False`.
# Condition D: The `AllowShareFile` setting is set to `False`.
# Condition E: The `AllowEgnyte` setting is set to `False`.
# Assuming that 'approvedProviders' is a list of approved cloud storage service names
# This list must be defined according to your organization's approved cloud storage services
# Retrieve the current Teams client configuration
$clientConfig = Get-CISMSTeamsOutput -Rec $recnum
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
# Testing
#$clientconfig.AllowGoogleDrive = $false
#$clientconfig.AllowBox = $false
#$clientconfig.AllowShareFile = $false
#$clientconfig.AllowEgnyte = $false
#$clientconfig.AllowDropBox = $false
# Define all possible cloud storage providers
$allProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
# If ApprovedCloudStorageProviders is provided, map it to the corresponding settings
if ($PSBoundParameters.ContainsKey('ApprovedCloudStorageProviders')) {
$approvedProviders = @()
foreach ($provider in $ApprovedCloudStorageProviders) {
$approvedProviders += "Allow$provider"
}
} else {
# Default approved providers
$approvedProviders = @()
}
$isCompliant = $true
$nonCompliantProviders = @()
foreach ($provider in $approvedProviders) {
if (-not $clientConfig.$provider) {
foreach ($provider in $allProviders) {
if ($clientConfig.$provider -and -not $approvedProviders.Contains($provider)) {
$isCompliant = $false
$nonCompliantProviders += $provider
}
}
$basePassDetails = "All cloud storage services are approved providers"
if ($ApprovedCloudStorageProviders) {
$basePassDetails = "Approved cloud storage services: $($ApprovedCloudStorageProviders -join ', ')"
}
# Create an instance of CISAuditResult and populate it
$params = @{
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" }
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { $basePassDetails }
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
}
$auditResult = Initialize-CISAuditResult @params
@@ -53,7 +70,6 @@ function Test-TeamsExternalFileSharing {
$auditResult = Get-TestError -LastError $LastError -recnum $recnum
}
}
end {
# Return auditResult
return $auditResult