Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
be0b6e0129 | ||
|
642cdfe2ab | ||
|
a8b76c7e16 | ||
|
fbf40fa98e | ||
|
f409e8a5f1 | ||
|
c341279531 | ||
|
6a8438bbe8 | ||
|
87c635210d | ||
|
07592569b4 | ||
|
4b3a0b7505 | ||
|
042bf7b37c | ||
|
69ae64562f | ||
|
c64325e773 | ||
|
c341db53c5 | ||
|
2f5c653cc8 | ||
|
00600123f3 | ||
|
0cb1643341 | ||
|
939980b087 | ||
|
f375fdd5ef | ||
|
0ea930c708 | ||
|
f9e3b5faed | ||
|
4613d592d1 | ||
|
da968db3e2 | ||
|
357f284d08 | ||
|
9e3058add4 | ||
|
d7d16ff0b5 | ||
|
45eb961554 | ||
|
686e805f6a | ||
|
63edc13261 | ||
|
9508130ddd | ||
|
db73d755ed |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,4 +16,5 @@ markdownissues.txt
|
||||
node_modules
|
||||
package-lock.json
|
||||
Aligned.xlsx
|
||||
test-gh1.ps1
|
||||
test-gh1.ps1
|
||||
ModdedModules/*
|
25
CHANGELOG.md
25
CHANGELOG.md
@@ -6,6 +6,31 @@ The format is based on and uses the types of changes according to [Keep a Change
|
||||
|
||||
### Fixed
|
||||
|
||||
- Get-SPOSite command to return all but voided output for no code runs (Ex: PowerAutomate)
|
||||
|
||||
## [0.1.27] - 2025-01-13
|
||||
|
||||
### Added
|
||||
|
||||
- Added additional error handling to connect function to identify problematic steps when they occur.
|
||||
- Added new method of verifying spo tenant for Connect-SPOService branch of connect function.
|
||||
- Added method to avoid "assembly already loaded" error in PNP Powershell function on first run, subsequent runs in the same session will still throw the error.
|
||||
|
||||
## [0.1.26] - 2024-08-04
|
||||
|
||||
### Added
|
||||
|
||||
- Added `New-M365SecurityAuditAuthObject` function to create a new authentication object for the security audit for app-based authentication.
|
||||
|
||||
### Changed
|
||||
|
||||
- Changed authentication options to include parameter for authenticating with a certificate.
|
||||
- Changed verbose output to ensure methods for suppressing all forms of output are available.
|
||||
|
||||
## [0.1.25] - 2024-07-23
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed test 1.3.1 as notification window for password expiration is no longer required.
|
||||
|
||||
## [0.1.24] - 2024-07-07
|
||||
|
@@ -1,5 +1,6 @@
|
||||
# M365FoundationsCISReport Module
|
||||
|
||||
[](https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport/actions/workflows/powershell.yml)
|
||||
[](https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport/actions/workflows/pages/pages-build-deployment)
|
||||
## License
|
||||
|
||||
This PowerShell module is based on CIS benchmarks and is distributed under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. This means:
|
||||
@@ -17,19 +18,47 @@ For full license details, please visit [Creative Commons Attribution-NonCommerci
|
||||
3. [Get-AdminRoleUserLicense](#Get-AdminRoleUserLicense)
|
||||
4. [Get-MFAStatus](#Get-MFAStatus)
|
||||
5. [Grant-M365SecurityAuditConsent](#Grant-M365SecurityAuditConsent)
|
||||
6. [Remove-RowsWithEmptyCSVStatus](#Remove-RowsWithEmptyCSVStatus)
|
||||
7. [Sync-CISExcelAndCsvData](#Sync-CISExcelAndCsvData)
|
||||
6. [New-M365SecurityAuditAuthObject](#New-M365SecurityAuditAuthObject)
|
||||
7. [Remove-RowsWithEmptyCSVStatus](#Remove-RowsWithEmptyCSVStatus)
|
||||
8. [Sync-CISExcelAndCsvData](#Sync-CISExcelAndCsvData)
|
||||
|
||||
## Invoke-M365SecurityAudit
|
||||
## Module Dependencies
|
||||
|
||||
The `M365FoundationsCISReport` module relies on several other PowerShell modules to perform its operations. The default run ensures these modules are installed with the specified versions. Use -NoModuleCheck to skip this step if you have installed the required modules previously and would like to suppress any output for automated runs.
|
||||
|
||||
### Required Modules for Audit Functions
|
||||
|
||||
Default modules used for audit functions:
|
||||
|
||||
- **ExchangeOnlineManagement**
|
||||
- Required Version: `3.3.0`
|
||||
|
||||
- **Microsoft.Graph**
|
||||
- Required Version: `2.4.0`
|
||||
|
||||
- **PnP.PowerShell** (Optional, if PnP App authentication is used for SharePoint Online)
|
||||
- Required Version: `2.5.0`
|
||||
|
||||
- **Microsoft.Online.SharePoint.PowerShell** (If PnP authentication is not used (Default) )
|
||||
- Required Version: `16.0.24009.12000`
|
||||
|
||||
- **MicrosoftTeams**
|
||||
- Required Version: `5.5.0`
|
||||
|
||||
- **ImportExcel** (If importing or exporting Excel files)
|
||||
- Required Version: `7.8.9`
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
```powershell
|
||||
# Example 1: Performing a security audit based on CIS benchmarks
|
||||
$auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com"
|
||||
$auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ApprovedCloudStorageProviders "DropBox" -ApprovedFederatedDomains "northwind.com"
|
||||
# Suppressed output for automated runs
|
||||
$auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -NoModuleCheck -NoModuleCheck -DoNotConfirmConnections -Confirm:$false
|
||||
|
||||
# Example 2: Exporting a security audit and it's nested tables to zipped CSV files
|
||||
Export-M365SecurityAuditTable -AuditResults $auditResults -ExportPath "C:\temp" -ExportOriginalTests -ExportAllTests
|
||||
Export-M365SecurityAuditTable -AuditResults $auditResults -ExportPath "C:\temp" -ExportOriginalTests -ExportNestedTables
|
||||
# Output Ex: 2024.07.07_14.55.55_M365FoundationsAudit_368B2E2F.zip
|
||||
|
||||
# Example 3: Retrieving licenses for users in administrative roles
|
||||
@@ -46,6 +75,15 @@ Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -CsvPath "path\to\data.c
|
||||
|
||||
# Example 7: Granting Microsoft Graph permissions to the auditor
|
||||
Grant-M365SecurityAuditConsent -UserPrincipalNameForConsent 'user@example.com'
|
||||
|
||||
# Example 8: (PowerShell 7.x Only) Creating a new authentication object for the security audit for app-based authentication.
|
||||
$authParams = New-M365SecurityAuditAuthObject `
|
||||
-ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" `
|
||||
-ClientId "12345678-1234-1234-1234-123456789012" `
|
||||
-TenantId "12345678-1234-1234-1234-123456789012" `
|
||||
-OnMicrosoftUrl "yourcompany.onmicrosoft.com" `
|
||||
-SpAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
Invoke-M365SecurityAudit -AuthParams $authParams -TenantAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
```
|
||||
|
||||
# NOTE
|
||||
@@ -57,4 +95,4 @@ If you encounter any issues while using the cmdlets, ensure that your environmen
|
||||
# SEE ALSO
|
||||
- [CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks/)
|
||||
- [Microsoft 365 Security Documentation](https://docs.microsoft.com/en-us/microsoft-365/security/)
|
||||
- [PowerShell Documentation](https://docs.microsoft.com/en-us/powershell/)
|
||||
- [PowerShell Documentation](https://docs.microsoft.com/en-us/powershell/)
|
||||
|
BIN
docs/index.html
BIN
docs/index.html
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version: https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Export-M365SecurityAuditTable
|
||||
@@ -15,24 +15,27 @@ Exports M365 security audit results to a CSV file or outputs a specific test res
|
||||
### OutputObjectFromAuditResultsSingle
|
||||
```
|
||||
Export-M365SecurityAuditTable [-AuditResults] <CISAuditResult[]> [-OutputTestNumber] <String>
|
||||
[<CommonParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### ExportAllResultsFromAuditResults
|
||||
```
|
||||
Export-M365SecurityAuditTable [-AuditResults] <CISAuditResult[]> [-ExportAllTests] -ExportPath <String>
|
||||
[-ExportOriginalTests] [-ExportToExcel] [<CommonParameters>]
|
||||
Export-M365SecurityAuditTable [-AuditResults] <CISAuditResult[]> [-ExportNestedTables] -ExportPath <String>
|
||||
[-ExportOriginalTests] [-ExportToExcel] [-Prefix <String>] [-ProgressAction <ActionPreference>] [-WhatIf]
|
||||
[-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### OutputObjectFromCsvSingle
|
||||
```
|
||||
Export-M365SecurityAuditTable [-CsvPath] <String> [-OutputTestNumber] <String> [<CommonParameters>]
|
||||
Export-M365SecurityAuditTable [-CsvPath] <String> [-OutputTestNumber] <String>
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### ExportAllResultsFromCsv
|
||||
```
|
||||
Export-M365SecurityAuditTable [-CsvPath] <String> [-ExportAllTests] -ExportPath <String> [-ExportOriginalTests]
|
||||
[-ExportToExcel] [<CommonParameters>]
|
||||
Export-M365SecurityAuditTable [-CsvPath] <String> [-ExportNestedTables] -ExportPath <String>
|
||||
[-ExportOriginalTests] [-ExportToExcel] [-Prefix <String>] [-ProgressAction <ActionPreference>] [-WhatIf]
|
||||
[-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -109,8 +112,24 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExportAllTests
|
||||
Switch to export all test results.
|
||||
### -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".
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: OutputObjectFromAuditResultsSingle, OutputObjectFromCsvSingle
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 2
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExportNestedTables
|
||||
Switch to export all test results. When specified, all test results are exported to the specified path.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
@@ -124,21 +143,6 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExportOriginalTests
|
||||
Switch to export the original audit results to a CSV file.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: ExportAllResultsFromAuditResults, ExportAllResultsFromCsv
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExportPath
|
||||
The path where the CSV files will be exported.
|
||||
|
||||
@@ -154,6 +158,21 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExportOriginalTests
|
||||
Switch to export the original audit results to a CSV file.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: ExportAllResultsFromAuditResults, ExportAllResultsFromCsv
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExportToExcel
|
||||
Switch to export the results to an Excel file.
|
||||
|
||||
@@ -169,17 +188,62 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -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".
|
||||
### -Prefix
|
||||
Add Prefix to filename after date when outputting to excel or csv.
|
||||
Validate that the count of letters in the prefix is less than 5.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: OutputObjectFromAuditResultsSingle, OutputObjectFromCsvSingle
|
||||
Parameter Sets: ExportAllResultsFromAuditResults, ExportAllResultsFromCsv
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 2
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: Corp
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -WhatIf
|
||||
Shows what would happen if the cmdlet runs. The cmdlet is not run.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases: wi
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -Confirm
|
||||
Prompts you for confirmation before running the cmdlet.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases: cf
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
@@ -191,7 +255,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
|
||||
## INPUTS
|
||||
|
||||
### [CISAuditResult[]] - An array of CISAuditResult objects.
|
||||
### [string] - A path to a CSV file.
|
||||
### [string] - A path to a CSV file.
|
||||
## OUTPUTS
|
||||
|
||||
### [PSCustomObject] - A custom object containing the path to the zip file and its hash.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version: https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
|
||||
@@ -13,7 +13,7 @@ Retrieves user licenses and roles for administrative accounts from Microsoft 365
|
||||
## SYNTAX
|
||||
|
||||
```
|
||||
Get-AdminRoleUserLicense [-SkipGraphConnection] [<CommonParameters>]
|
||||
Get-AdminRoleUserLicense [-SkipGraphConnection] [-ProgressAction <ActionPreference>] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -54,6 +54,21 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### CommonParameters
|
||||
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||
|
||||
@@ -63,7 +78,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
|
||||
## OUTPUTS
|
||||
|
||||
### PSCustomObject
|
||||
### Returns a custom object for each user with administrative roles that includes the following properties: RoleName, UserName, UserPrincipalName, UserId, HybridUser, and Licenses.
|
||||
### Returns a custom object for each user with administrative roles that includes the following properties: RoleName, UserName, UserPrincipalName, UserId, HybridUser, and Licenses.
|
||||
## NOTES
|
||||
Creation Date: 2024-04-15
|
||||
Purpose/Change: Initial function development to support Microsoft 365 administrative role auditing.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version: https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-MFAStatus
|
||||
@@ -13,7 +13,8 @@ Retrieves the MFA (Multi-Factor Authentication) status for Azure Active Director
|
||||
## SYNTAX
|
||||
|
||||
```
|
||||
Get-MFAStatus [[-UserId] <String>] [-SkipMSOLConnectionChecks] [<CommonParameters>]
|
||||
Get-MFAStatus [[-UserId] <String>] [-SkipMSOLConnectionChecks] [-ProgressAction <ActionPreference>]
|
||||
[<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -36,21 +37,6 @@ Retrieves the MFA status for the specified user with the UPN "example@domain.com
|
||||
|
||||
## PARAMETERS
|
||||
|
||||
### -SkipMSOLConnectionChecks
|
||||
{{ Fill SkipMSOLConnectionChecks Description }}
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -UserId
|
||||
The User Principal Name (UPN) of a specific user to retrieve MFA status for.
|
||||
If not provided, the function retrieves MFA status for all users.
|
||||
@@ -67,6 +53,36 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -SkipMSOLConnectionChecks
|
||||
{{ Fill SkipMSOLConnectionChecks Description }}
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### CommonParameters
|
||||
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||
|
||||
@@ -75,14 +91,14 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
|
||||
## OUTPUTS
|
||||
|
||||
### System.Object
|
||||
### Returns a sorted list of custom objects containing the following properties:
|
||||
### - UserPrincipalName
|
||||
### - DisplayName
|
||||
### - MFAState
|
||||
### - MFADefaultMethod
|
||||
### - MFAPhoneNumber
|
||||
### - PrimarySMTP
|
||||
### - Aliases
|
||||
### Returns a sorted list of custom objects containing the following properties:
|
||||
### - UserPrincipalName
|
||||
### - DisplayName
|
||||
### - MFAState
|
||||
### - MFADefaultMethod
|
||||
### - MFAPhoneNumber
|
||||
### - PrimarySMTP
|
||||
### - Aliases
|
||||
## NOTES
|
||||
The function requires the MSOL module to be installed and connected to your tenant.
|
||||
Ensure that you have the necessary permissions to read user and MFA status information.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version: https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Grant-M365SecurityAuditConsent
|
||||
@@ -14,7 +14,8 @@ Grants Microsoft Graph permissions for an auditor.
|
||||
|
||||
```
|
||||
Grant-M365SecurityAuditConsent [-UserPrincipalNameForConsent] <String> [-SkipGraphConnection]
|
||||
[-SkipModuleCheck] [-SuppressRevertOutput] [-DoNotDisconnect] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-SkipModuleCheck] [-SuppressRevertOutput] [-DoNotDisconnect] [-ProgressAction <ActionPreference>] [-WhatIf]
|
||||
[-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -40,18 +41,18 @@ Grants Microsoft Graph permissions to user@example.com, skipping the connection
|
||||
|
||||
## PARAMETERS
|
||||
|
||||
### -DoNotDisconnect
|
||||
If specified, does not disconnect from Microsoft Graph after granting consent.
|
||||
### -UserPrincipalNameForConsent
|
||||
Specify the UPN of the user to grant consent for.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Required: True
|
||||
Position: 1
|
||||
Default value: None
|
||||
Accept pipeline input: True (ByPropertyName, ByValue)
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
@@ -100,18 +101,34 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -UserPrincipalNameForConsent
|
||||
Specify the UPN of the user to grant consent for.
|
||||
### -DoNotDisconnect
|
||||
If specified, does not disconnect from Microsoft Graph after granting consent.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 1
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -WhatIf
|
||||
Shows what would happen if the cmdlet runs.
|
||||
The cmdlet is not run.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases: wi
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: True (ByPropertyName, ByValue)
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
@@ -130,14 +147,13 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -WhatIf
|
||||
Shows what would happen if the cmdlet runs.
|
||||
The cmdlet is not run.
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: wi
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version: https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||
@@ -16,14 +16,16 @@ Invokes a security audit for Microsoft 365 environments.
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>]
|
||||
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-AuthParams <CISAuthenticationParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### ELevelFilter
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -ELevel <String>
|
||||
-ProfileLevel <String> [-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>]
|
||||
[-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm]
|
||||
[-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections]
|
||||
[-AuthParams <CISAuthenticationParameters>] [-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm]
|
||||
[<CommonParameters>]
|
||||
```
|
||||
|
||||
@@ -31,35 +33,40 @@ Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -ELev
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG1]
|
||||
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-AuthParams <CISAuthenticationParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### IG2Filter
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG2]
|
||||
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-AuthParams <CISAuthenticationParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### IG3Filter
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] [-IncludeIG3]
|
||||
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-AuthParams <CISAuthenticationParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### RecFilter
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -IncludeRecommendation <String[]>
|
||||
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-AuthParams <CISAuthenticationParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
### SkipRecFilter
|
||||
```
|
||||
Invoke-M365SecurityAudit [-TenantAdminUrl <String>] [-DomainName <String>] -SkipRecommendation <String[]>
|
||||
[-ApprovedCloudStorageProviders <String[]>] [-ApprovedFederatedDomains <String[]>] [-DoNotConnect]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
[-DoNotDisconnect] [-NoModuleCheck] [-DoNotConfirmConnections] [-AuthParams <CISAuthenticationParameters>]
|
||||
[-ProgressAction <ActionPreference>] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -165,26 +172,12 @@ What if: Performing the operation "Invoke-M365SecurityAudit" on target "Microsof
|
||||
|
||||
## PARAMETERS
|
||||
|
||||
### -ApprovedCloudStorageProviders
|
||||
Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.
|
||||
### -TenantAdminUrl
|
||||
The URL of the tenant admin.
|
||||
If not specified, none of the SharePoint Online tests will run.
|
||||
|
||||
```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[]
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
@@ -210,51 +203,6 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -DoNotConfirmConnections
|
||||
If specified, the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -DoNotConnect
|
||||
If specified, the cmdlet will not establish a connection to Microsoft 365 services.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -DoNotDisconnect
|
||||
If specified, the cmdlet will not disconnect from Microsoft 365 services after execution.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ELevel
|
||||
Specifies the E-Level (E3 or E5) for the audit.
|
||||
This parameter is optional and can be combined with the ProfileLevel parameter.
|
||||
@@ -271,6 +219,22 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProfileLevel
|
||||
Specifies the profile level (L1 or L2) for the audit.
|
||||
This parameter is optional and can be combined with the ELevel parameter.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: ELevelFilter
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -IncludeIG1
|
||||
If specified, includes tests where IG1 is true.
|
||||
|
||||
@@ -332,37 +296,6 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -NoModuleCheck
|
||||
If specified, the cmdlet will not check for the presence of required modules.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProfileLevel
|
||||
Specifies the profile level (L1 or L2) for the audit.
|
||||
This parameter is optional and can be combined with the ELevel parameter.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: ELevelFilter
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -SkipRecommendation
|
||||
Specifies specific recommendations to exclude from the audit.
|
||||
Accepts an array of recommendation numbers.
|
||||
@@ -379,12 +312,26 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -TenantAdminUrl
|
||||
The URL of the tenant admin.
|
||||
If not specified, none of the SharePoint Online tests will run.
|
||||
### -ApprovedCloudStorageProviders
|
||||
Specifies the approved cloud storage providers for the audit. Accepts an array of cloud storage provider names.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
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:
|
||||
|
||||
@@ -395,13 +342,73 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -Confirm
|
||||
Prompts you for confirmation before running the cmdlet.
|
||||
### -DoNotConnect
|
||||
If specified, the cmdlet will not establish a connection to Microsoft 365 services.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases: cf
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -DoNotDisconnect
|
||||
If specified, the cmdlet will not disconnect from Microsoft 365 services after execution.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -NoModuleCheck
|
||||
If specified, the cmdlet will not check for the presence of required modules.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -DoNotConfirmConnections
|
||||
If specified, the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: False
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -AuthParams
|
||||
Specifies an authentication object containing parameters for application-based authentication. If provided, this will be used for connecting to services.
|
||||
|
||||
```yaml
|
||||
Type: CISAuthenticationParameters
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
@@ -426,6 +433,36 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -Confirm
|
||||
Prompts you for confirmation before running the cmdlet.
|
||||
|
||||
```yaml
|
||||
Type: SwitchParameter
|
||||
Parameter Sets: (All)
|
||||
Aliases: cf
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### CommonParameters
|
||||
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||
|
||||
@@ -435,7 +472,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable
|
||||
## OUTPUTS
|
||||
|
||||
### CISAuditResult[]
|
||||
### The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
||||
### The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
||||
## NOTES
|
||||
- This module is based on CIS benchmarks.
|
||||
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||
|
@@ -26,6 +26,9 @@ Grants Microsoft Graph permissions for an auditor.
|
||||
### [Invoke-M365SecurityAudit](Invoke-M365SecurityAudit)
|
||||
Invokes a security audit for Microsoft 365 environments.
|
||||
|
||||
### [New-M365SecurityAuditAuthObject](New-M365SecurityAuditAuthObject)
|
||||
Creates a new CISAuthenticationParameters object for Microsoft 365 authentication.
|
||||
|
||||
### [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.
|
||||
|
||||
|
149
help/New-M365SecurityAuditAuthObject.md
Normal file
149
help/New-M365SecurityAuditAuthObject.md
Normal file
@@ -0,0 +1,149 @@
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version:
|
||||
schema: 2.0.0
|
||||
---
|
||||
|
||||
# New-M365SecurityAuditAuthObject
|
||||
|
||||
## SYNOPSIS
|
||||
Creates a new CISAuthenticationParameters object for Microsoft 365 authentication.
|
||||
|
||||
## SYNTAX
|
||||
|
||||
```
|
||||
New-M365SecurityAuditAuthObject [-ClientCertThumbPrint] <String> [-ClientId] <String> [-TenantId] <String>
|
||||
[-OnMicrosoftUrl] <String> [-SpAdminUrl] <String> [-ProgressAction <ActionPreference>] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
The New-M365SecurityAuditAuthObject function constructs a new CISAuthenticationParameters object
|
||||
containing the necessary credentials and URLs for authenticating to various Microsoft 365 services.
|
||||
It validates input parameters to ensure they conform to expected formats and length requirements.
|
||||
An app registration in Azure AD with the required permissions to EXO, SPO, MSTeams and MgGraph is needed.
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
### EXAMPLE 1
|
||||
```
|
||||
$authParams = New-M365SecurityAuditAuthObject -ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" `
|
||||
-ClientId "12345678-1234-1234-1234-123456789012" `
|
||||
-TenantId "12345678-1234-1234-1234-123456789012" `
|
||||
-OnMicrosoftUrl "yourcompany.onmicrosoft.com" `
|
||||
-SpAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
Creates a new CISAuthenticationParameters object with the specified credentials and URLs, validating each parameter's format and length.
|
||||
```
|
||||
|
||||
## PARAMETERS
|
||||
|
||||
### -ClientCertThumbPrint
|
||||
The thumbprint of the client certificate used for authentication.
|
||||
It must be a 40-character hexadecimal string.
|
||||
This certificate is used to authenticate the application in Azure AD.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 1
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ClientId
|
||||
The Client ID (Application ID) of the Azure AD application.
|
||||
It must be a valid GUID format.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 2
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -TenantId
|
||||
The Tenant ID of the Azure AD directory.
|
||||
It must be a valid GUID format representing your Microsoft 365 tenant.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 3
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -OnMicrosoftUrl
|
||||
The URL of your onmicrosoft.com domain.
|
||||
It should be in the format 'example.onmicrosoft.com'.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 4
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -SpAdminUrl
|
||||
The SharePoint admin URL, which should end with '-admin.sharepoint.com'.
|
||||
This URL is used for connecting to SharePoint Online.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: True
|
||||
Position: 5
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### CommonParameters
|
||||
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||
|
||||
## INPUTS
|
||||
|
||||
### None. You cannot pipe objects to this function.
|
||||
## OUTPUTS
|
||||
|
||||
### CISAuthenticationParameters
|
||||
### The function returns an instance of the CISAuthenticationParameters class containing the authentication details.
|
||||
## NOTES
|
||||
Requires PowerShell 7.0 or later.
|
||||
|
||||
## RELATED LINKS
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version:
|
||||
@@ -13,7 +13,8 @@ Removes rows from an Excel worksheet where the 'CSV_Status' column is empty and
|
||||
## SYNTAX
|
||||
|
||||
```
|
||||
Remove-RowsWithEmptyCSVStatus [-FilePath] <String> [-WorksheetName] <String> [<CommonParameters>]
|
||||
Remove-RowsWithEmptyCSVStatus [-FilePath] <String> [-WorksheetName] <String>
|
||||
[-ProgressAction <ActionPreference>] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -60,6 +61,21 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### CommonParameters
|
||||
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
external help file: M365FoundationsCISReport-help.xml
|
||||
Module Name: M365FoundationsCISReport
|
||||
online version: https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
||||
@@ -14,7 +14,7 @@ Synchronizes and updates data in an Excel worksheet with new information from a
|
||||
|
||||
```
|
||||
Sync-CISExcelAndCsvData [[-ExcelPath] <String>] [[-CsvPath] <String>] [[-SheetName] <String>]
|
||||
[<CommonParameters>]
|
||||
[-ProgressAction <ActionPreference>] [<CommonParameters>]
|
||||
```
|
||||
|
||||
## DESCRIPTION
|
||||
@@ -32,22 +32,6 @@ Updates the 'AuditData' worksheet in 'excel.xlsx' with data from 'data.csv', add
|
||||
|
||||
## PARAMETERS
|
||||
|
||||
### -CsvPath
|
||||
Specifies the path to the CSV file containing new data.
|
||||
This parameter is mandatory.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: 2
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ExcelPath
|
||||
Specifies the path to the Excel file to be updated.
|
||||
This parameter is mandatory.
|
||||
@@ -64,6 +48,22 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -CsvPath
|
||||
Specifies the path to the CSV file containing new data.
|
||||
This parameter is mandatory.
|
||||
|
||||
```yaml
|
||||
Type: String
|
||||
Parameter Sets: (All)
|
||||
Aliases:
|
||||
|
||||
Required: False
|
||||
Position: 2
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -SheetName
|
||||
Specifies the name of the worksheet in the Excel file where data will be merged and updated.
|
||||
This parameter is mandatory.
|
||||
@@ -80,13 +80,28 @@ Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### -ProgressAction
|
||||
{{ Fill ProgressAction Description }}
|
||||
|
||||
```yaml
|
||||
Type: ActionPreference
|
||||
Parameter Sets: (All)
|
||||
Aliases: proga
|
||||
|
||||
Required: False
|
||||
Position: Named
|
||||
Default value: None
|
||||
Accept pipeline input: False
|
||||
Accept wildcard characters: False
|
||||
```
|
||||
|
||||
### CommonParameters
|
||||
This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216).
|
||||
|
||||
## INPUTS
|
||||
|
||||
### System.String
|
||||
### The function accepts strings for file paths and worksheet names.
|
||||
### The function accepts strings for file paths and worksheet names.
|
||||
## OUTPUTS
|
||||
|
||||
### None
|
||||
|
@@ -38,6 +38,14 @@ Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -CsvPath "path\to\data.c
|
||||
|
||||
# Example 7: Granting Microsoft Graph permissions to the auditor
|
||||
Grant-M365SecurityAuditConsent -UserPrincipalNameForConsent 'user@example.com'
|
||||
|
||||
# Example 8: (PowerShell 7.x Only) Creating a new authentication object for the security audit for app-based authentication.
|
||||
$authParams = New-M365SecurityAuditAuthObject -ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" `
|
||||
-ClientId "12345678-1234-1234-1234-123456789012" `
|
||||
-TenantId "12345678-1234-1234-1234-123456789012" `
|
||||
-OnMicrosoftUrl "yourcompany.onmicrosoft.com" `
|
||||
-SpAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
Invoke-M365SecurityAudit -AuthParams $authParams -TenantAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
```
|
||||
|
||||
# NOTE
|
||||
|
@@ -1,14 +1,14 @@
|
||||
Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
||||
. .\source\Classes\CISAuditResult.ps1
|
||||
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir docs -template ".\helpers\psDoc-master\src\out-html-template.ps1"
|
||||
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir ".\" -template ".\helpers\psDoc-master\src\out-markdown-template.ps1" -fileName ".\README.md" -
|
||||
.\helpers\psDoc-master\src\psDoc.ps1 -moduleName M365FoundationsCISReport -outputDir ".\" -template ".\helpers\psDoc-master\src\out-markdown-template.ps1" -fileName ".\README.md"
|
||||
|
||||
|
||||
<#
|
||||
$ver = "v0.1.24"
|
||||
$ver = "v0.1.28"
|
||||
git checkout main
|
||||
git pull origin main
|
||||
git tag -a $ver -m "Release version $ver refactor Update"
|
||||
git tag -a $ver -m "Release version $ver bugfix Update"
|
||||
git push origin $ver
|
||||
"Fix: PR #37"
|
||||
git push origin $ver
|
||||
@@ -53,8 +53,8 @@ Register-SecretVault -Name ModuleBuildCreds -ModuleName `
|
||||
"SecretManagement.JustinGrote.CredMan" -ErrorAction Stop
|
||||
|
||||
|
||||
Set-Secret -Name "GalleryApiToken" -Vault ModuleBuildCreds
|
||||
Set-Secret -Name "GitHubToken" -Vault ModuleBuildCreds
|
||||
#Set-Secret -Name "GalleryApiToken" -Vault ModuleBuildCreds
|
||||
#Set-Secret -Name "GitHubToken" -Vault ModuleBuildCreds
|
||||
|
||||
|
||||
$GalleryApiToken = Get-Secret -Name "GalleryApiToken" -Vault ModuleBuildCreds -AsPlainText
|
||||
|
43
source/Classes/CISAuthenticationParameters.ps1
Normal file
43
source/Classes/CISAuthenticationParameters.ps1
Normal file
@@ -0,0 +1,43 @@
|
||||
class CISAuthenticationParameters {
|
||||
[string]$ClientCertThumbPrint
|
||||
[string]$ClientId
|
||||
[string]$TenantId
|
||||
[string]$OnMicrosoftUrl
|
||||
[string]$SpAdminUrl
|
||||
|
||||
# Constructor with validation
|
||||
CISAuthenticationParameters(
|
||||
[string]$ClientCertThumbPrint,
|
||||
[string]$ClientId,
|
||||
[string]$TenantId,
|
||||
[string]$OnMicrosoftUrl,
|
||||
[string]$SpAdminUrl
|
||||
) {
|
||||
# Validate ClientCertThumbPrint
|
||||
if (-not $ClientCertThumbPrint -or $ClientCertThumbPrint.Length -ne 40 -or $ClientCertThumbPrint -notmatch '^[0-9a-fA-F]{40}$') {
|
||||
throw [ArgumentException]::new("ClientCertThumbPrint must be a 40-character hexadecimal string.")
|
||||
}
|
||||
# Validate ClientId
|
||||
if (-not $ClientId -or $ClientId -notmatch '^[0-9a-fA-F\-]{36}$') {
|
||||
throw [ArgumentException]::new("ClientId must be a valid GUID in the format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'.")
|
||||
}
|
||||
# Validate TenantId
|
||||
if (-not $TenantId -or $TenantId -notmatch '^[0-9a-fA-F\-]{36}$') {
|
||||
throw [ArgumentException]::new("TenantId must be a valid GUID in the format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'.")
|
||||
}
|
||||
# Validate OnMicrosoftUrl
|
||||
if (-not $OnMicrosoftUrl -or $OnMicrosoftUrl -notmatch '^[a-zA-Z0-9]+\.onmicrosoft\.com$') {
|
||||
throw [ArgumentException]::new("OnMicrosoftUrl must be in the format 'example.onmicrosoft.com'.")
|
||||
}
|
||||
# Validate SpAdminUrl
|
||||
if (-not $SpAdminUrl -or $SpAdminUrl -notmatch '^https:\/\/[a-zA-Z0-9\-]+\-admin\.sharepoint\.com$') {
|
||||
throw [ArgumentException]::new("SpAdminUrl must be in the format 'https://[name]-admin.sharepoint.com'.")
|
||||
}
|
||||
# Assign validated properties
|
||||
$this.ClientCertThumbPrint = $ClientCertThumbPrint
|
||||
$this.ClientId = $ClientId
|
||||
$this.TenantId = $TenantId
|
||||
$this.OnMicrosoftUrl = $OnMicrosoftUrl
|
||||
$this.SpAdminUrl = $SpAdminUrl
|
||||
}
|
||||
}
|
@@ -33,7 +33,7 @@ Copyright = '(c) 2024 Douglas S. Rios (DrIOSx). All rights reserved.'
|
||||
Description = 'Automated assessment of 50 CIS 365 Foundations v3.0.0 benchmark.'
|
||||
|
||||
# Minimum version of the Windows PowerShell engine required by this module
|
||||
PowerShellVersion = '5.0'
|
||||
# PowerShellVersion = '5.1'
|
||||
|
||||
# Name of the Windows PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
@@ -51,7 +51,7 @@ PowerShellVersion = '5.0'
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
RequiredModules = @()
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
@@ -1,37 +1,49 @@
|
||||
function Assert-ModuleAvailability {
|
||||
[CmdletBinding()]
|
||||
[OutputType([void]) ]
|
||||
param(
|
||||
[string]$ModuleName,
|
||||
[string]$RequiredVersion,
|
||||
[string[]]$SubModules = @()
|
||||
)
|
||||
|
||||
try {
|
||||
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
||||
|
||||
if ($null -eq $module) {
|
||||
Write-Host "Installing $ModuleName module..." -ForegroundColor Yellow
|
||||
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
||||
process {
|
||||
try {
|
||||
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
||||
if ($null -eq $module) {
|
||||
Write-Verbose "Installing $ModuleName module..."
|
||||
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
||||
}
|
||||
elseif ($module.Version -lt [version]$RequiredVersion) {
|
||||
Write-Verbose "Updating $ModuleName module to required version..."
|
||||
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
||||
}
|
||||
else {
|
||||
Write-Verbose "$ModuleName module is already at required version or newer."
|
||||
}
|
||||
if ($ModuleName -eq "Microsoft.Graph") {
|
||||
# "Preloading Microsoft.Graph assembly to prevent type-loading issues..."
|
||||
Write-Verbose "Preloading Microsoft.Graph assembly to prevent type-loading issues..."
|
||||
try {
|
||||
# Run a harmless cmdlet to preload the assembly
|
||||
Get-MgGroup -Top 1 -ErrorAction SilentlyContinue | Out-Null
|
||||
}
|
||||
catch {
|
||||
Write-Verbose "Could not preload Microsoft.Graph assembly. Error: $_"
|
||||
}
|
||||
}
|
||||
if ($SubModules.Count -gt 0) {
|
||||
foreach ($subModule in $SubModules) {
|
||||
Write-Verbose "Importing submodule $ModuleName.$subModule..."
|
||||
Get-Module "$ModuleName.$subModule" | Import-Module -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Importing module $ModuleName..."
|
||||
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
|
||||
}
|
||||
}
|
||||
elseif ($module.Version -lt [version]$RequiredVersion) {
|
||||
Write-Host "Updating $ModuleName module to required version..." -ForegroundColor Yellow
|
||||
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
||||
}
|
||||
else {
|
||||
Write-Host "$ModuleName module is already at required version or newer." -ForegroundColor Gray
|
||||
}
|
||||
|
||||
if ($SubModules.Count -gt 0) {
|
||||
foreach ($subModule in $SubModules) {
|
||||
Write-Host "Importing submodule $ModuleName.$subModule..." -ForegroundColor DarkGray
|
||||
Import-Module -Name "$ModuleName.$subModule" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||
}
|
||||
} else {
|
||||
Write-Host "Importing module $ModuleName..." -ForegroundColor DarkGray
|
||||
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null
|
||||
catch {
|
||||
throw "Assert-ModuleAvailability:`n$_"
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "An error occurred with module $ModuleName`: $_"
|
||||
}
|
||||
}
|
||||
}
|
@@ -5,6 +5,9 @@ function Connect-M365Suite {
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$TenantAdminUrl,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[CISAuthenticationParameters]$AuthParams,
|
||||
|
||||
[Parameter(Mandatory)]
|
||||
[string[]]$RequiredConnections,
|
||||
|
||||
@@ -12,111 +15,131 @@ function Connect-M365Suite {
|
||||
[switch]$SkipConfirmation
|
||||
)
|
||||
|
||||
$VerbosePreference = "SilentlyContinue"
|
||||
$VerbosePreference = if ($SkipConfirmation) { 'SilentlyContinue' } else { 'Continue' }
|
||||
$tenantInfo = @()
|
||||
$connectedServices = @()
|
||||
|
||||
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 Yellow
|
||||
Connect-AzureAD -WarningAction SilentlyContinue | Out-Null
|
||||
$tenantDetails = Get-AzureADTenantDetail -WarningAction SilentlyContinue
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = "Azure Active Directory"
|
||||
TenantName = $tenantDetails.DisplayName
|
||||
TenantID = $tenantDetails.ObjectId
|
||||
}
|
||||
$connectedServices += "AzureAD"
|
||||
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "EXO | Microsoft Graph") {
|
||||
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Yellow
|
||||
if ($RequiredConnections -contains 'Microsoft Graph' -or $RequiredConnections -contains 'EXO | Microsoft Graph') {
|
||||
try {
|
||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
||||
Write-Verbose 'Connecting to Microsoft Graph...'
|
||||
if ($AuthParams) {
|
||||
Connect-MgGraph -CertificateThumbprint $AuthParams.ClientCertThumbPrint -AppId $AuthParams.ClientId -TenantId $AuthParams.TenantId -NoWelcome | Out-Null
|
||||
}
|
||||
else {
|
||||
Connect-MgGraph -Scopes 'Directory.Read.All', 'Domain.Read.All', 'Policy.Read.All', 'Organization.Read.All' -NoWelcome | Out-Null
|
||||
}
|
||||
$graphOrgDetails = Get-MgOrganization
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = "Microsoft Graph"
|
||||
Service = 'Microsoft Graph'
|
||||
TenantName = $graphOrgDetails.DisplayName
|
||||
TenantID = $graphOrgDetails.Id
|
||||
TenantID = $graphOrgDetails.Id
|
||||
}
|
||||
$connectedServices += "Microsoft Graph"
|
||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||
$connectedServices += 'Microsoft Graph'
|
||||
Write-Verbose 'Successfully connected to Microsoft Graph.'
|
||||
}
|
||||
catch {
|
||||
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
|
||||
$graphOrgDetails = Get-MgOrganization
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = "Microsoft Graph"
|
||||
TenantName = $graphOrgDetails.DisplayName
|
||||
TenantID = $graphOrgDetails.Id
|
||||
throw "Failed to connect to Microsoft Graph: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($RequiredConnections -contains 'EXO' -or $RequiredConnections -contains 'AzureAD | EXO' -or $RequiredConnections -contains 'Microsoft Teams | EXO' -or $RequiredConnections -contains 'EXO | Microsoft Graph') {
|
||||
try {
|
||||
Write-Verbose 'Connecting to Exchange Online...'
|
||||
if ($AuthParams) {
|
||||
Connect-ExchangeOnline -AppId $AuthParams.ClientId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -Organization $AuthParams.OnMicrosoftUrl -ShowBanner:$false | Out-Null
|
||||
}
|
||||
$connectedServices += "Microsoft Graph"
|
||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||
else {
|
||||
Connect-ExchangeOnline -ShowBanner:$false | Out-Null
|
||||
}
|
||||
$exoTenant = (Get-OrganizationConfig).Identity
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = 'Exchange Online'
|
||||
TenantName = $exoTenant
|
||||
TenantID = 'N/A'
|
||||
}
|
||||
$connectedServices += 'EXO'
|
||||
Write-Verbose 'Successfully connected to Exchange Online.'
|
||||
}
|
||||
catch {
|
||||
throw "Failed to connect to Exchange Online: $($_.Exception.Message)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "EXO | Microsoft Graph") {
|
||||
Write-Host "Connecting to Exchange Online..." -ForegroundColor Yellow
|
||||
Connect-ExchangeOnline -ShowBanner:$false | Out-Null
|
||||
$exoTenant = (Get-OrganizationConfig).Identity
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = "Exchange Online"
|
||||
TenantName = $exoTenant
|
||||
TenantID = "N/A"
|
||||
if ($RequiredConnections -contains 'SPO') {
|
||||
try {
|
||||
Write-Verbose 'Connecting to SharePoint Online...'
|
||||
if ($AuthParams) {
|
||||
Connect-PnPOnline -Url $AuthParams.SpAdminUrl -ClientId $AuthParams.ClientId -Tenant $AuthParams.OnMicrosoftUrl -Thumbprint $AuthParams.ClientCertThumbPrint | Out-Null
|
||||
}
|
||||
else {
|
||||
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
||||
}
|
||||
$tenantName = if ($AuthParams) {
|
||||
(Get-PnPSite).Url
|
||||
}
|
||||
else {
|
||||
# Supress output from Get-SPOSite for powerautomate to avoid errors
|
||||
[void]($sites = Get-SPOSite -Limit All)
|
||||
# Get the URL from the first site collection
|
||||
$url = $sites[0].Url
|
||||
# Use regex to extract the base URL up to the .com portion
|
||||
$baseUrl = [regex]::Match($url, 'https://[^/]+.com').Value
|
||||
# Output the base URL
|
||||
$baseUrl
|
||||
}
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = 'SharePoint Online'
|
||||
TenantName = $tenantName
|
||||
}
|
||||
$connectedServices += 'SPO'
|
||||
Write-Verbose 'Successfully connected to SharePoint Online.'
|
||||
}
|
||||
catch {
|
||||
throw "Failed to connect to SharePoint Online: $($_.Exception.Message)"
|
||||
}
|
||||
$connectedServices += "EXO"
|
||||
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($RequiredConnections -contains "SPO") {
|
||||
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Yellow
|
||||
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
||||
$spoContext = Get-SPOCrossTenantHostUrl
|
||||
$tenantName = Get-UrlLine -Output $spoContext
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = "SharePoint Online"
|
||||
TenantName = $tenantName
|
||||
if ($RequiredConnections -contains 'Microsoft Teams' -or $RequiredConnections -contains 'Microsoft Teams | EXO') {
|
||||
try {
|
||||
Write-Verbose 'Connecting to Microsoft Teams...'
|
||||
if ($AuthParams) {
|
||||
Connect-MicrosoftTeams -TenantId $AuthParams.TenantId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -ApplicationId $AuthParams.ClientId | Out-Null
|
||||
}
|
||||
else {
|
||||
Connect-MicrosoftTeams | Out-Null
|
||||
}
|
||||
$teamsTenantDetails = Get-CsTenant
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = 'Microsoft Teams'
|
||||
TenantName = $teamsTenantDetails.DisplayName
|
||||
TenantID = $teamsTenantDetails.TenantId
|
||||
}
|
||||
$connectedServices += 'Microsoft Teams'
|
||||
Write-Verbose 'Successfully connected to Microsoft Teams.'
|
||||
}
|
||||
catch {
|
||||
throw "Failed to connect to Microsoft Teams: $($_.Exception.Message)"
|
||||
}
|
||||
$connectedServices += "SPO"
|
||||
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Yellow
|
||||
Connect-MicrosoftTeams | Out-Null
|
||||
$teamsTenantDetails = Get-CsTenant
|
||||
$tenantInfo += [PSCustomObject]@{
|
||||
Service = "Microsoft Teams"
|
||||
TenantName = $teamsTenantDetails.DisplayName
|
||||
TenantID = $teamsTenantDetails.TenantId
|
||||
}
|
||||
$connectedServices += "Microsoft Teams"
|
||||
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Display tenant information and confirm with the user
|
||||
if (-not $SkipConfirmation) {
|
||||
Write-Host "Connected to the following tenants:" -ForegroundColor Yellow
|
||||
Write-Verbose 'Connected to the following tenants:'
|
||||
foreach ($tenant in $tenantInfo) {
|
||||
Write-Host "Service: $($tenant.Service)" -ForegroundColor Cyan
|
||||
Write-Host "Tenant Context: $($tenant.TenantName)`n" -ForegroundColor Green
|
||||
#Write-Host "Tenant ID: $($tenant.TenantID)"
|
||||
Write-Verbose "Service: $($tenant.Service) | Tenant: $($tenant.TenantName)"
|
||||
}
|
||||
$confirmation = Read-Host "Do you want to proceed with these connections? (Y/N)"
|
||||
$confirmation = Read-Host 'Do you want to proceed with these connections? (Y/N)'
|
||||
if ($confirmation -notlike 'Y') {
|
||||
Write-Host "Connection setup aborted by user." -ForegroundColor Red
|
||||
Disconnect-M365Suite -RequiredConnections $connectedServices
|
||||
throw "User aborted connection setup."
|
||||
throw 'User aborted connection setup.'
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$VerbosePreference = "Continue"
|
||||
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
|
||||
throw $_
|
||||
$VerbosePreference = 'Continue'
|
||||
throw "Connection failed: $($_.Exception.Message)"
|
||||
}
|
||||
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
finally {
|
||||
$VerbosePreference = 'Continue'
|
||||
}
|
||||
}
|
@@ -8,7 +8,7 @@ function Disconnect-M365Suite {
|
||||
# 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
|
||||
Write-Verbose "Disconnecting from Exchange Online..."
|
||||
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ function Disconnect-M365Suite {
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
|
||||
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
|
||||
Write-Verbose "Disconnecting from Azure AD..."
|
||||
Disconnect-AzureAD | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ function Disconnect-M365Suite {
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "Microsoft Graph") {
|
||||
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
||||
Write-Verbose "Disconnecting from Microsoft Graph..."
|
||||
Disconnect-MgGraph | Out-Null
|
||||
}
|
||||
}
|
||||
@@ -38,8 +38,14 @@ function Disconnect-M365Suite {
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "SPO") {
|
||||
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
|
||||
Disconnect-SPOService | Out-Null
|
||||
if (($script:PnpAuth)) {
|
||||
Write-Verbose "Disconnecting from PnPOnline..."
|
||||
Disconnect-PnPOnline | Out-Null
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Disconnecting from SharePoint Online..."
|
||||
Disconnect-SPOService | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
catch {
|
||||
@@ -48,13 +54,12 @@ function Disconnect-M365Suite {
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
|
||||
Write-Verbose "Disconnecting from Microsoft Teams..."
|
||||
Disconnect-MicrosoftTeams | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
|
||||
}
|
||||
|
||||
Write-Host "All necessary sessions have been disconnected." -ForegroundColor Green
|
||||
Write-Verbose "All necessary sessions have been disconnected."
|
||||
}
|
@@ -46,437 +46,442 @@ function Get-CISExoOutput {
|
||||
#>
|
||||
}
|
||||
process {
|
||||
Write-Verbose "Get-CISExoOutput: Retuning data for Rec: $Rec"
|
||||
switch ($Rec) {
|
||||
'1.2.2' {
|
||||
# Test-BlockSharedMailboxSignIn.ps1
|
||||
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
||||
# [object[]]
|
||||
# $MBX mock object:
|
||||
<#
|
||||
$MBX = @(
|
||||
[PSCustomObject]@{
|
||||
UserPrincipalName = "SMBuser1@domain.com"
|
||||
ExternalDirectoryObjectId = "123e4567-e89b-12d3-a456-426614174000"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
UserPrincipalName = "SMBuser2@domain.com"
|
||||
ExternalDirectoryObjectId = "987e6543-21ba-12d3-a456-426614174000"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
UserPrincipalName = "SMBuser3@domain.com"
|
||||
ExternalDirectoryObjectId = "abcddcba-98fe-76dc-a456-426614174000"
|
||||
}
|
||||
)
|
||||
#>
|
||||
return $MBX.ExternalDirectoryObjectId
|
||||
}
|
||||
'1.3.3' {
|
||||
# Test-ExternalSharingCalendars.ps1
|
||||
# Step: Retrieve sharing policies related to calendar sharing
|
||||
# $sharingPolicies Mock Object
|
||||
<#
|
||||
$sharingPolicies = [PSCustomObject]@{
|
||||
Name = "Default Sharing Policy"
|
||||
Domains = @("Anonymous:CalendarSharingFreeBusySimple")
|
||||
Enabled = $true
|
||||
Default = $true
|
||||
}
|
||||
#>
|
||||
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
||||
# [psobject[]]
|
||||
return $sharingPolicies
|
||||
}
|
||||
'1.3.6' {
|
||||
# Test-CustomerLockbox.ps1
|
||||
# Step: Retrieve the organization configuration (Condition C: Pass/Fail)
|
||||
# $orgConfig Mock Object:
|
||||
<#
|
||||
# return $orgConfig
|
||||
$orgConfig = [PSCustomObject]@{
|
||||
CustomerLockBoxEnabled = $true
|
||||
}
|
||||
#>
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
||||
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
||||
# [bool]
|
||||
return $customerLockboxEnabled
|
||||
}
|
||||
'2.1.1' {
|
||||
# Test-SafeLinksOfficeApps.ps1
|
||||
if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) {
|
||||
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||
# Retrieve all Safe Links policies
|
||||
# $policies Mock Object:
|
||||
try {
|
||||
Write-Verbose "Get-CISExoOutput: Retuning data for Rec: $Rec"
|
||||
switch ($Rec) {
|
||||
'1.2.2' {
|
||||
# Test-BlockSharedMailboxSignIn.ps1
|
||||
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
||||
# [object[]]
|
||||
# $MBX mock object:
|
||||
<#
|
||||
$policies = @(
|
||||
$MBX = @(
|
||||
[PSCustomObject]@{
|
||||
Name = "PolicyOne"
|
||||
EnableSafeLinksForEmail = $true
|
||||
EnableSafeLinksForTeams = $true
|
||||
EnableSafeLinksForOffice = $true
|
||||
TrackClicks = $true
|
||||
AllowClickThrough = $false
|
||||
UserPrincipalName = "SMBuser1@domain.com"
|
||||
ExternalDirectoryObjectId = "123e4567-e89b-12d3-a456-426614174000"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "PolicyTwo"
|
||||
EnableSafeLinksForEmail = $true
|
||||
EnableSafeLinksForTeams = $true
|
||||
EnableSafeLinksForOffice = $true
|
||||
TrackClicks = $true
|
||||
AllowClickThrough = $true
|
||||
UserPrincipalName = "SMBuser2@domain.com"
|
||||
ExternalDirectoryObjectId = "987e6543-21ba-12d3-a456-426614174000"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "PolicyThree"
|
||||
EnableSafeLinksForEmail = $true
|
||||
EnableSafeLinksForTeams = $true
|
||||
EnableSafeLinksForOffice = $true
|
||||
TrackClicks = $true
|
||||
AllowClickThrough = $false
|
||||
UserPrincipalName = "SMBuser3@domain.com"
|
||||
ExternalDirectoryObjectId = "abcddcba-98fe-76dc-a456-426614174000"
|
||||
}
|
||||
)
|
||||
#>
|
||||
$policies = Get-SafeLinksPolicy
|
||||
# Initialize the details collection
|
||||
$misconfiguredDetails = @()
|
||||
foreach ($policy in $policies) {
|
||||
# Get the detailed configuration of each policy
|
||||
$policyDetails = $policy #Get-SafeLinksPolicy -Identity $policy.Name
|
||||
# Check each required property and record failures
|
||||
# Condition A: Checking policy settings
|
||||
$failures = @()
|
||||
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On
|
||||
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On
|
||||
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On
|
||||
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On
|
||||
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off
|
||||
# Only add details for policies that have misconfigurations
|
||||
if ($failures.Count -gt 0) {
|
||||
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
||||
}
|
||||
}
|
||||
# [object[]]
|
||||
return $misconfiguredDetails
|
||||
return $MBX.ExternalDirectoryObjectId
|
||||
}
|
||||
else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'2.1.2' {
|
||||
# Test-CommonAttachmentFilter.ps1
|
||||
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
||||
# Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center.
|
||||
# Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`.
|
||||
# Retrieve the attachment filter policy
|
||||
# $attachmentFilter Mock Object
|
||||
<#
|
||||
$attachmentFilter = [PSCustomObject]@{
|
||||
EnableFileFilter = $true
|
||||
}
|
||||
#>
|
||||
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
||||
$result = $attachmentFilter.EnableFileFilter
|
||||
# [bool]
|
||||
return $result
|
||||
}
|
||||
'2.1.3' {
|
||||
# Test-NotifyMalwareInternal.ps1
|
||||
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
||||
# Retrieve all 'Custom' malware filter policies and check notification settings
|
||||
# $malwareNotifications Mock Object
|
||||
<#
|
||||
$malwareNotifications = @(
|
||||
[PSCustomObject]@{
|
||||
Identity = "Default"
|
||||
EnableInternalSenderAdminNotifications = $true
|
||||
RecommendedPolicyType = "Custom"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Anti-malware-Policy"
|
||||
EnableInternalSenderAdminNotifications = $true
|
||||
RecommendedPolicyType = "Custom"
|
||||
}
|
||||
)
|
||||
#>
|
||||
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
||||
# [object[]]
|
||||
return $malwareNotifications
|
||||
}
|
||||
'2.1.4' {
|
||||
# Test-SafeAttachmentsPolicy.ps1
|
||||
if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) {
|
||||
# Retrieve all Safe Attachment policies where Enable is set to True
|
||||
# Check if ErrorAction needed below
|
||||
# $safeAttachmentPolicies Mock Object:
|
||||
'1.3.3' {
|
||||
# Test-ExternalSharingCalendars.ps1
|
||||
# Step: Retrieve sharing policies related to calendar sharing
|
||||
# $sharingPolicies Mock Object
|
||||
<#
|
||||
$safeAttachmentPolicies = @(
|
||||
[PSCustomObject]@{
|
||||
Policy = "Strict Preset Security Policy"
|
||||
Action = "Block"
|
||||
QuarantineTag = "AdminOnlyAccessPolicy"
|
||||
Redirect = $false
|
||||
Enabled = $true
|
||||
}
|
||||
)
|
||||
$sharingPolicies = [PSCustomObject]@{
|
||||
Name = "Default Sharing Policy"
|
||||
Domains = @("Anonymous:CalendarSharingFreeBusySimple")
|
||||
Enabled = $true
|
||||
Default = $true
|
||||
}
|
||||
#>
|
||||
$safeAttachmentPolicies = Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue | Where-Object { $_.Enable -eq $true }
|
||||
$safeAttachmentRules = Get-SafeAttachmentRule
|
||||
# [object[]]
|
||||
return $safeAttachmentPolicies, $safeAttachmentRules
|
||||
else {
|
||||
return 1,1
|
||||
}
|
||||
}
|
||||
}
|
||||
'2.1.5' {
|
||||
# Test-SafeAttachmentsTeams.ps1
|
||||
if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) {
|
||||
# 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
|
||||
$atpPolicies = Get-AtpPolicyForO365
|
||||
# Check if the required ATP policies are enabled
|
||||
# $atpPolicyResult Mock Object:
|
||||
<#
|
||||
$atpPolicyResult = @(
|
||||
[PSCustomObject]@{
|
||||
Name = "Default"
|
||||
EnableATPForSPOTeamsODB = $true
|
||||
EnableSafeDocs = $true
|
||||
AllowSafeDocsOpen = $false
|
||||
}
|
||||
)
|
||||
#>
|
||||
$atpPolicyResult = $atpPolicies | Where-Object {
|
||||
$_.EnableATPForSPOTeamsODB -eq $true -and
|
||||
$_.EnableSafeDocs -eq $true -and
|
||||
$_.AllowSafeDocsOpen -eq $false
|
||||
}
|
||||
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
||||
# [psobject[]]
|
||||
return $atpPolicyResult
|
||||
return $sharingPolicies
|
||||
}
|
||||
else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'2.1.6' {
|
||||
# Test-SpamPolicyAdminNotify.ps1
|
||||
# Retrieve the hosted outbound spam filter policies
|
||||
# $spamPolicies Mock Object:
|
||||
<#
|
||||
# Mock data representing multiple spam filter policies
|
||||
$spamPolicies = @(
|
||||
[PSCustomObject]@{
|
||||
Name = "Default"
|
||||
IsDefault = $true
|
||||
NotifyOutboundSpam = $true
|
||||
BccSuspiciousOutboundMail = $true
|
||||
NotifyOutboundSpamRecipients = "admin@example.com"
|
||||
BccSuspiciousOutboundAdditionalRecipients = "bccadmin@example.com"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "Custom Policy 1"
|
||||
IsDefault = $false
|
||||
NotifyOutboundSpam = $false
|
||||
BccSuspiciousOutboundMail = $true
|
||||
NotifyOutboundSpamRecipients = ""
|
||||
BccSuspiciousOutboundAdditionalRecipients = ""
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "Custom Policy 2"
|
||||
IsDefault = $false
|
||||
NotifyOutboundSpam = $true
|
||||
BccSuspiciousOutboundMail = $false
|
||||
NotifyOutboundSpamRecipients = "notify@example.com"
|
||||
BccSuspiciousOutboundAdditionalRecipients = "bccnotify@example.com"
|
||||
'1.3.6' {
|
||||
# Test-CustomerLockbox.ps1
|
||||
# Step: Retrieve the organization configuration (Condition C: Pass/Fail)
|
||||
# $orgConfig Mock Object:
|
||||
<#
|
||||
# return $orgConfig
|
||||
$orgConfig = [PSCustomObject]@{
|
||||
CustomerLockBoxEnabled = $true
|
||||
}
|
||||
)
|
||||
#>
|
||||
$spamPolicies = Get-HostedOutboundSpamFilterPolicy
|
||||
return $spamPolicies
|
||||
}
|
||||
'2.1.7' {
|
||||
# Test-AntiPhishingPolicy.ps1
|
||||
<#
|
||||
$antiPhishPolicies = @(
|
||||
[PSCustomObject]@{
|
||||
Identity = "Strict Preset Security Policy"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 4
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = "John Doe;jdoe@contoso.net, Jane Does;janedoe@contoso.net"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Office365 AntiPhish Default"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 2
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = $null # Assuming it targets all users as it's the default
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Admin"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 2
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = $null # Assuming it targets all users
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Standard Preset Security Policy"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 3
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = $null # Assuming it targets all users
|
||||
}
|
||||
)
|
||||
#>
|
||||
$antiPhishPolicies = Get-AntiPhishPolicy
|
||||
return $antiPhishPolicies
|
||||
}
|
||||
'2.1.9' {
|
||||
# Test-EnableDKIM.ps1
|
||||
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
|
||||
# Retrieve DKIM configuration for all domains
|
||||
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
|
||||
# [object[]]
|
||||
return $dkimConfig
|
||||
}
|
||||
'3.1.1' {
|
||||
# Test-AuditLogSearch.ps1
|
||||
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
||||
# Retrieve the audit log configuration
|
||||
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
||||
#
|
||||
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
||||
# [bool]
|
||||
return $auditLogResult
|
||||
}
|
||||
'6.1.1' {
|
||||
# Test-AuditDisabledFalse.ps1
|
||||
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
||||
# Retrieve the AuditDisabled configuration (Condition B)
|
||||
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
||||
# [bool]
|
||||
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
||||
return $auditNotDisabled
|
||||
}
|
||||
'6.1.2' {
|
||||
# Test-MailboxAuditingE3.ps1
|
||||
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||
# [object[]]
|
||||
return $mailboxes
|
||||
}
|
||||
'6.1.3' {
|
||||
# Test-MailboxAuditingE5.ps1
|
||||
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||
# [object[]]
|
||||
return $mailboxes
|
||||
}
|
||||
'6.2.1' {
|
||||
# Test-BlockMailForwarding.ps1
|
||||
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||
# Step 1: Retrieve the transport rules that redirect messages
|
||||
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
||||
if ($null -eq $transportRules) {
|
||||
$transportRules = 1
|
||||
#>
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
||||
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
||||
# [bool]
|
||||
return $customerLockboxEnabled
|
||||
}
|
||||
# Step 2: Check all anti-spam outbound policies
|
||||
$outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy
|
||||
$nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' }
|
||||
return $transportRules, $nonCompliantSpamPolicies
|
||||
}
|
||||
'6.2.2' {
|
||||
# Test-NoWhitelistDomains.ps1
|
||||
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
||||
# Retrieve transport rules that whitelist specific domains
|
||||
# Condition A: Checking for transport rules that whitelist specific domains
|
||||
# [object[]]
|
||||
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
|
||||
return $whitelistedRules
|
||||
}
|
||||
'6.2.3' {
|
||||
# Test-IdentifyExternalEmail.ps1
|
||||
# 6.2.3 (L1) Ensure email from external senders is identified
|
||||
# Retrieve external sender tagging configuration
|
||||
# [object[]]
|
||||
$externalInOutlook = Get-ExternalInOutlook
|
||||
return $externalInOutlook
|
||||
}
|
||||
'6.3.1' {
|
||||
# Test-RestrictOutlookAddins.ps1
|
||||
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
||||
$customPolicyFailures = @()
|
||||
# Check all mailboxes for custom policies with unallowed add-ins
|
||||
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
|
||||
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
|
||||
foreach ($policy in $roleAssignmentPolicies) {
|
||||
if ($policy.RoleAssignmentPolicy) {
|
||||
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
|
||||
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
||||
# Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users.
|
||||
if ($foundRoles) {
|
||||
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
|
||||
'2.1.1' {
|
||||
# Test-SafeLinksOfficeApps.ps1
|
||||
if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) {
|
||||
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||
# Retrieve all Safe Links policies
|
||||
# $policies Mock Object:
|
||||
<#
|
||||
$policies = @(
|
||||
[PSCustomObject]@{
|
||||
Name = "PolicyOne"
|
||||
EnableSafeLinksForEmail = $true
|
||||
EnableSafeLinksForTeams = $true
|
||||
EnableSafeLinksForOffice = $true
|
||||
TrackClicks = $true
|
||||
AllowClickThrough = $false
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "PolicyTwo"
|
||||
EnableSafeLinksForEmail = $true
|
||||
EnableSafeLinksForTeams = $true
|
||||
EnableSafeLinksForOffice = $true
|
||||
TrackClicks = $true
|
||||
AllowClickThrough = $true
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "PolicyThree"
|
||||
EnableSafeLinksForEmail = $true
|
||||
EnableSafeLinksForTeams = $true
|
||||
EnableSafeLinksForOffice = $true
|
||||
TrackClicks = $true
|
||||
AllowClickThrough = $false
|
||||
}
|
||||
)
|
||||
#>
|
||||
$policies = Get-SafeLinksPolicy
|
||||
# Initialize the details collection
|
||||
$misconfiguredDetails = @()
|
||||
foreach ($policy in $policies) {
|
||||
# Get the detailed configuration of each policy
|
||||
$policyDetails = $policy #Get-SafeLinksPolicy -Identity $policy.Name
|
||||
# Check each required property and record failures
|
||||
# Condition A: Checking policy settings
|
||||
$failures = @()
|
||||
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On
|
||||
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On
|
||||
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On
|
||||
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On
|
||||
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off
|
||||
# Only add details for policies that have misconfigurations
|
||||
if ($failures.Count -gt 0) {
|
||||
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
||||
}
|
||||
}
|
||||
# [object[]]
|
||||
return $misconfiguredDetails
|
||||
}
|
||||
else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'2.1.2' {
|
||||
# Test-CommonAttachmentFilter.ps1
|
||||
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
||||
# Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center.
|
||||
# Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`.
|
||||
# Retrieve the attachment filter policy
|
||||
# $attachmentFilter Mock Object
|
||||
<#
|
||||
$attachmentFilter = [PSCustomObject]@{
|
||||
EnableFileFilter = $true
|
||||
}
|
||||
#>
|
||||
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
||||
$result = $attachmentFilter.EnableFileFilter
|
||||
# [bool]
|
||||
return $result
|
||||
}
|
||||
'2.1.3' {
|
||||
# Test-NotifyMalwareInternal.ps1
|
||||
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
||||
# Retrieve all 'Custom' malware filter policies and check notification settings
|
||||
# $malwareNotifications Mock Object
|
||||
<#
|
||||
$malwareNotifications = @(
|
||||
[PSCustomObject]@{
|
||||
Identity = "Default"
|
||||
EnableInternalSenderAdminNotifications = $true
|
||||
RecommendedPolicyType = "Custom"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Anti-malware-Policy"
|
||||
EnableInternalSenderAdminNotifications = $true
|
||||
RecommendedPolicyType = "Custom"
|
||||
}
|
||||
)
|
||||
#>
|
||||
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
||||
# [object[]]
|
||||
return $malwareNotifications
|
||||
}
|
||||
'2.1.4' {
|
||||
# Test-SafeAttachmentsPolicy.ps1
|
||||
if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) {
|
||||
# Retrieve all Safe Attachment policies where Enable is set to True
|
||||
# Check if ErrorAction needed below
|
||||
# $safeAttachmentPolicies Mock Object:
|
||||
<#
|
||||
$safeAttachmentPolicies = @(
|
||||
[PSCustomObject]@{
|
||||
Policy = "Strict Preset Security Policy"
|
||||
Action = "Block"
|
||||
QuarantineTag = "AdminOnlyAccessPolicy"
|
||||
Redirect = $false
|
||||
Enabled = $true
|
||||
}
|
||||
)
|
||||
#>
|
||||
$safeAttachmentPolicies = Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue | Where-Object { $_.Enable -eq $true }
|
||||
$safeAttachmentRules = Get-SafeAttachmentRule
|
||||
# [object[]]
|
||||
return $safeAttachmentPolicies, $safeAttachmentRules
|
||||
else {
|
||||
return 1,1
|
||||
}
|
||||
}
|
||||
}
|
||||
'2.1.5' {
|
||||
# Test-SafeAttachmentsTeams.ps1
|
||||
if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) {
|
||||
# 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
|
||||
$atpPolicies = Get-AtpPolicyForO365
|
||||
# Check if the required ATP policies are enabled
|
||||
# $atpPolicyResult Mock Object:
|
||||
<#
|
||||
$atpPolicyResult = @(
|
||||
[PSCustomObject]@{
|
||||
Name = "Default"
|
||||
EnableATPForSPOTeamsODB = $true
|
||||
EnableSafeDocs = $true
|
||||
AllowSafeDocsOpen = $false
|
||||
}
|
||||
)
|
||||
#>
|
||||
$atpPolicyResult = $atpPolicies | Where-Object {
|
||||
$_.EnableATPForSPOTeamsODB -eq $true -and
|
||||
$_.EnableSafeDocs -eq $true -and
|
||||
$_.AllowSafeDocsOpen -eq $false
|
||||
}
|
||||
# [psobject[]]
|
||||
return $atpPolicyResult
|
||||
}
|
||||
else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
'2.1.6' {
|
||||
# Test-SpamPolicyAdminNotify.ps1
|
||||
# Retrieve the hosted outbound spam filter policies
|
||||
# $spamPolicies Mock Object:
|
||||
<#
|
||||
# Mock data representing multiple spam filter policies
|
||||
$spamPolicies = @(
|
||||
[PSCustomObject]@{
|
||||
Name = "Default"
|
||||
IsDefault = $true
|
||||
NotifyOutboundSpam = $true
|
||||
BccSuspiciousOutboundMail = $true
|
||||
NotifyOutboundSpamRecipients = "admin@example.com"
|
||||
BccSuspiciousOutboundAdditionalRecipients = "bccadmin@example.com"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "Custom Policy 1"
|
||||
IsDefault = $false
|
||||
NotifyOutboundSpam = $false
|
||||
BccSuspiciousOutboundMail = $true
|
||||
NotifyOutboundSpamRecipients = ""
|
||||
BccSuspiciousOutboundAdditionalRecipients = ""
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Name = "Custom Policy 2"
|
||||
IsDefault = $false
|
||||
NotifyOutboundSpam = $true
|
||||
BccSuspiciousOutboundMail = $false
|
||||
NotifyOutboundSpamRecipients = "notify@example.com"
|
||||
BccSuspiciousOutboundAdditionalRecipients = "bccnotify@example.com"
|
||||
}
|
||||
)
|
||||
#>
|
||||
$spamPolicies = Get-HostedOutboundSpamFilterPolicy
|
||||
return $spamPolicies
|
||||
}
|
||||
'2.1.7' {
|
||||
# Test-AntiPhishingPolicy.ps1
|
||||
<#
|
||||
$antiPhishPolicies = @(
|
||||
[PSCustomObject]@{
|
||||
Identity = "Strict Preset Security Policy"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 4
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = "John Doe;jdoe@contoso.net, Jane Does;janedoe@contoso.net"
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Office365 AntiPhish Default"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 2
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = $null # Assuming it targets all users as it's the default
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Admin"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 2
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = $null # Assuming it targets all users
|
||||
},
|
||||
[PSCustomObject]@{
|
||||
Identity = "Standard Preset Security Policy"
|
||||
Enabled = $true
|
||||
PhishThresholdLevel = 3
|
||||
EnableMailboxIntelligenceProtection = $true
|
||||
EnableMailboxIntelligence = $true
|
||||
EnableSpoofIntelligence = $true
|
||||
TargetedUsersToProtect = $null # Assuming it targets all users
|
||||
}
|
||||
)
|
||||
#>
|
||||
$antiPhishPolicies = Get-AntiPhishPolicy
|
||||
return $antiPhishPolicies
|
||||
}
|
||||
'2.1.9' {
|
||||
# Test-EnableDKIM.ps1
|
||||
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
|
||||
# Retrieve DKIM configuration for all domains
|
||||
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
|
||||
# [object[]]
|
||||
return $dkimConfig
|
||||
}
|
||||
'3.1.1' {
|
||||
# Test-AuditLogSearch.ps1
|
||||
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
||||
# Retrieve the audit log configuration
|
||||
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
||||
#
|
||||
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
||||
# [bool]
|
||||
return $auditLogResult
|
||||
}
|
||||
'6.1.1' {
|
||||
# Test-AuditDisabledFalse.ps1
|
||||
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
||||
# Retrieve the AuditDisabled configuration (Condition B)
|
||||
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
||||
# [bool]
|
||||
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
||||
return $auditNotDisabled
|
||||
}
|
||||
'6.1.2' {
|
||||
# Test-MailboxAuditingE3.ps1
|
||||
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||
# [object[]]
|
||||
return $mailboxes
|
||||
}
|
||||
'6.1.3' {
|
||||
# Test-MailboxAuditingE5.ps1
|
||||
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||
# [object[]]
|
||||
return $mailboxes
|
||||
}
|
||||
'6.2.1' {
|
||||
# Test-BlockMailForwarding.ps1
|
||||
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||
# Step 1: Retrieve the transport rules that redirect messages
|
||||
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
||||
if ($null -eq $transportRules) {
|
||||
$transportRules = 1
|
||||
}
|
||||
# Step 2: Check all anti-spam outbound policies
|
||||
$outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy
|
||||
$nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' }
|
||||
return $transportRules, $nonCompliantSpamPolicies
|
||||
}
|
||||
'6.2.2' {
|
||||
# Test-NoWhitelistDomains.ps1
|
||||
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
||||
# Retrieve transport rules that whitelist specific domains
|
||||
# Condition A: Checking for transport rules that whitelist specific domains
|
||||
# [object[]]
|
||||
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
|
||||
return $whitelistedRules
|
||||
}
|
||||
'6.2.3' {
|
||||
# Test-IdentifyExternalEmail.ps1
|
||||
# 6.2.3 (L1) Ensure email from external senders is identified
|
||||
# Retrieve external sender tagging configuration
|
||||
# [object[]]
|
||||
$externalInOutlook = Get-ExternalInOutlook
|
||||
return $externalInOutlook
|
||||
}
|
||||
'6.3.1' {
|
||||
# Test-RestrictOutlookAddins.ps1
|
||||
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
||||
$customPolicyFailures = @()
|
||||
# Check all mailboxes for custom policies with unallowed add-ins
|
||||
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
|
||||
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
|
||||
foreach ($policy in $roleAssignmentPolicies) {
|
||||
if ($policy.RoleAssignmentPolicy) {
|
||||
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
|
||||
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
||||
# Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users.
|
||||
if ($foundRoles) {
|
||||
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Check Default Role Assignment Policy
|
||||
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
|
||||
return $customPolicyFailures, $defaultPolicy
|
||||
}
|
||||
# Check Default Role Assignment Policy
|
||||
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
|
||||
return $customPolicyFailures, $defaultPolicy
|
||||
'6.5.1' {
|
||||
# Test-ModernAuthExchangeOnline.ps1
|
||||
# Ensuring the ExchangeOnlineManagement module is available
|
||||
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
||||
# Check modern authentication setting in Exchange Online configuration (Condition A and B)
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
|
||||
return $orgConfig
|
||||
}
|
||||
'6.5.2' {
|
||||
# Test-MailTipsEnabled.ps1
|
||||
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
||||
# Retrieve organization configuration for MailTips settings
|
||||
# [object]
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
||||
return $orgConfig
|
||||
}
|
||||
'6.5.3' {
|
||||
# Test-RestrictStorageProvidersOutlook.ps1
|
||||
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
||||
# Retrieve all OwaMailbox policies
|
||||
# [object[]]
|
||||
$owaPolicies = Get-OwaMailboxPolicy
|
||||
return $owaPolicies
|
||||
}
|
||||
'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 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
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
'6.5.1' {
|
||||
# Test-ModernAuthExchangeOnline.ps1
|
||||
# Ensuring the ExchangeOnlineManagement module is available
|
||||
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
||||
# Check modern authentication setting in Exchange Online configuration (Condition A and B)
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
|
||||
return $orgConfig
|
||||
}
|
||||
'6.5.2' {
|
||||
# Test-MailTipsEnabled.ps1
|
||||
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
||||
# Retrieve organization configuration for MailTips settings
|
||||
# [object]
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
||||
return $orgConfig
|
||||
}
|
||||
'6.5.3' {
|
||||
# Test-RestrictStorageProvidersOutlook.ps1
|
||||
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
||||
# Retrieve all OwaMailbox policies
|
||||
# [object[]]
|
||||
$owaPolicies = Get-OwaMailboxPolicy
|
||||
return $owaPolicies
|
||||
}
|
||||
'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 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
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
catch {
|
||||
throw "Get-CISExoOutput: `n$_"
|
||||
}
|
||||
}
|
||||
end {
|
||||
|
@@ -34,296 +34,301 @@ function Get-CISMSTeamsOutput {
|
||||
#>
|
||||
}
|
||||
process {
|
||||
Write-Verbose "Get-CISMSTeamsOutput: Retuning data for Rec: $Rec"
|
||||
switch ($Rec) {
|
||||
'8.1.1' {
|
||||
# Test-TeamsExternalFileSharing.ps1
|
||||
# 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
|
||||
try {
|
||||
Write-Verbose "Get-CISMSTeamsOutput: Retuning data for Rec: $Rec"
|
||||
switch ($Rec) {
|
||||
'8.1.1' {
|
||||
# Test-TeamsExternalFileSharing.ps1
|
||||
# 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`.
|
||||
# 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
|
||||
# Add option for approved providers.
|
||||
$clientConfig = Get-CsTeamsClientConfiguration
|
||||
return $clientConfig
|
||||
}
|
||||
'8.1.2' {
|
||||
# Test-BlockChannelEmails.ps1
|
||||
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
||||
#
|
||||
# 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: The `AllowEmailIntoChannel` setting in Teams is set to `False`.
|
||||
# - Condition B: The setting `Users can send emails to a channel email address` is set to `Off` in the Teams admin center.
|
||||
# - Condition C: Verification using PowerShell confirms that the `AllowEmailIntoChannel` setting is disabled.
|
||||
#
|
||||
# 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 `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.
|
||||
# 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
|
||||
}
|
||||
'8.1.2' {
|
||||
# Test-BlockChannelEmails.ps1
|
||||
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
||||
#
|
||||
# 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: The `AllowEmailIntoChannel` setting in Teams is set to `False`.
|
||||
# - Condition B: The setting `Users can send emails to a channel email address` is set to `Off` in the Teams admin center.
|
||||
# - Condition C: Verification using PowerShell confirms that the `AllowEmailIntoChannel` setting is disabled.
|
||||
#
|
||||
# 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 `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-CsTeamsClientConfiguration -Identity Global
|
||||
return $teamsClientConfig
|
||||
# Retrieve Teams client configuration
|
||||
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
|
||||
return $teamsClientConfig
|
||||
}
|
||||
'8.2.1' {
|
||||
# Test-TeamsExternalAccess.ps1
|
||||
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
|
||||
#
|
||||
# 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: 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 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
|
||||
}
|
||||
'8.5.1' {
|
||||
# Test-NoAnonymousMeetingJoin.ps1
|
||||
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
|
||||
#
|
||||
# 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: `AllowAnonymousUsersToJoinMeeting` is set to `False`.
|
||||
# - Condition B: Verification using the UI confirms that `Anonymous users can join a meeting` is set to `Off` in the Global meeting policy.
|
||||
# - Condition C: PowerShell command output indicates that anonymous users are not allowed to join meetings.
|
||||
#
|
||||
# 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: `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
|
||||
}
|
||||
'8.5.2' {
|
||||
# Test-NoAnonymousMeetingStart.ps1
|
||||
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
|
||||
#
|
||||
# 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: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is set to `False`.
|
||||
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting is configured to ensure they must wait in the lobby.
|
||||
# - Condition C: Verification using the UI confirms that the setting `Anonymous users and dial-in callers can start a meeting` is set to `Off`.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.3' {
|
||||
# Test-OrgOnlyBypassLobby.ps1
|
||||
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
|
||||
#
|
||||
# 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: The `AutoAdmittedUsers` setting in the Teams meeting policy is set to `EveryoneInCompanyExcludingGuests`.
|
||||
# - Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
|
||||
# - Condition C: Verification using the Microsoft Teams admin center confirms that the meeting join & lobby settings are configured as recommended.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.4' {
|
||||
# Test-DialInBypassLobby.ps1
|
||||
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
||||
#
|
||||
# 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: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is set to `False`.
|
||||
# - Condition B: Verification using the UI in the Microsoft Teams admin center confirms that "People dialing in can't bypass the lobby" is set to `Off`.
|
||||
# - Condition C: Ensure that individuals who dial in by phone must wait in the lobby until admitted by a meeting organizer, co-organizer, or presenter.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.5' {
|
||||
# Test-MeetingChatNoAnonymous.ps1
|
||||
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
||||
#
|
||||
# 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: The `MeetingChatEnabledType` setting in Teams is set to `EnabledExceptAnonymous`.
|
||||
# - Condition B: The setting for meeting chat is configured to allow chat for everyone except anonymous users.
|
||||
# - Condition C: Verification using the Teams Admin Center confirms that the meeting chat settings are configured as recommended.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.6' {
|
||||
# Test-OrganizersPresent.ps1
|
||||
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
|
||||
#
|
||||
# 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: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is set to `OrganizerOnlyUserOverride`.
|
||||
# - Condition B: Verification using the Teams admin center confirms that the setting "Who can present" is configured to "Only organizers and co-organizers".
|
||||
# - Condition C: Verification using PowerShell confirms that the `DesignatedPresenterRoleMode` is set to `OrganizerOnlyUserOverride`.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.7' {
|
||||
# Test-ExternalNoControl.ps1
|
||||
# 8.5.7 (L1) Ensure external participants can't give or request control
|
||||
#
|
||||
# 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: Ensure the `AllowExternalParticipantGiveRequestControl` setting in Teams is 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 confirms that external participants are unable to give or request control.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
'8.2.1' {
|
||||
# Test-TeamsExternalAccess.ps1
|
||||
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
|
||||
#
|
||||
# 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: 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 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
|
||||
}
|
||||
'8.5.1' {
|
||||
# Test-NoAnonymousMeetingJoin.ps1
|
||||
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
|
||||
#
|
||||
# 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: `AllowAnonymousUsersToJoinMeeting` is set to `False`.
|
||||
# - Condition B: Verification using the UI confirms that `Anonymous users can join a meeting` is set to `Off` in the Global meeting policy.
|
||||
# - Condition C: PowerShell command output indicates that anonymous users are not allowed to join meetings.
|
||||
#
|
||||
# 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: `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
|
||||
}
|
||||
'8.5.2' {
|
||||
# Test-NoAnonymousMeetingStart.ps1
|
||||
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
|
||||
#
|
||||
# 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: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is set to `False`.
|
||||
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting is configured to ensure they must wait in the lobby.
|
||||
# - Condition C: Verification using the UI confirms that the setting `Anonymous users and dial-in callers can start a meeting` is set to `Off`.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.3' {
|
||||
# Test-OrgOnlyBypassLobby.ps1
|
||||
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
|
||||
#
|
||||
# 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: The `AutoAdmittedUsers` setting in the Teams meeting policy is set to `EveryoneInCompanyExcludingGuests`.
|
||||
# - Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
|
||||
# - Condition C: Verification using the Microsoft Teams admin center confirms that the meeting join & lobby settings are configured as recommended.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.4' {
|
||||
# Test-DialInBypassLobby.ps1
|
||||
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
||||
#
|
||||
# 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: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is set to `False`.
|
||||
# - Condition B: Verification using the UI in the Microsoft Teams admin center confirms that "People dialing in can't bypass the lobby" is set to `Off`.
|
||||
# - Condition C: Ensure that individuals who dial in by phone must wait in the lobby until admitted by a meeting organizer, co-organizer, or presenter.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.5' {
|
||||
# Test-MeetingChatNoAnonymous.ps1
|
||||
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
||||
#
|
||||
# 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: The `MeetingChatEnabledType` setting in Teams is set to `EnabledExceptAnonymous`.
|
||||
# - Condition B: The setting for meeting chat is configured to allow chat for everyone except anonymous users.
|
||||
# - Condition C: Verification using the Teams Admin Center confirms that the meeting chat settings are configured as recommended.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.6' {
|
||||
# Test-OrganizersPresent.ps1
|
||||
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
|
||||
#
|
||||
# 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: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is set to `OrganizerOnlyUserOverride`.
|
||||
# - Condition B: Verification using the Teams admin center confirms that the setting "Who can present" is configured to "Only organizers and co-organizers".
|
||||
# - Condition C: Verification using PowerShell confirms that the `DesignatedPresenterRoleMode` is set to `OrganizerOnlyUserOverride`.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
'8.5.7' {
|
||||
# Test-ExternalNoControl.ps1
|
||||
# 8.5.7 (L1) Ensure external participants can't give or request control
|
||||
#
|
||||
# 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: Ensure the `AllowExternalParticipantGiveRequestControl` setting in Teams is 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 confirms that external participants are unable to give or request control.
|
||||
#
|
||||
# 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 `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
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
catch {
|
||||
throw "Get-CISMSTeamsOutput: `n$_"
|
||||
}
|
||||
}
|
||||
end {
|
||||
|
@@ -38,76 +38,86 @@ function Get-CISMgOutput {
|
||||
#>
|
||||
}
|
||||
process {
|
||||
Write-Verbose "Get-CISMgOutput: Retuning data for Rec: $Rec"
|
||||
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
|
||||
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
||||
return $globalAdmins
|
||||
}
|
||||
'1.2.1' {
|
||||
# Test-ManagedApprovedPublicGroups
|
||||
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
||||
return $allGroups
|
||||
}
|
||||
'1.3.1' {
|
||||
# Test-PasswordNeverExpirePolicy.ps1
|
||||
$domains = if ($DomainName) {
|
||||
Get-MgDomain -DomainId $DomainName
|
||||
try {
|
||||
Write-Verbose "Get-CISMgOutput: Retuning data for Rec: $Rec"
|
||||
switch ($rec) {
|
||||
'1.1.1' {
|
||||
# 1.1.1
|
||||
# Test-AdministrativeAccountCompliance
|
||||
$AdminRoleAssignmentsAndUsers = Get-AdminRoleUserAndAssignment
|
||||
return $AdminRoleAssignmentsAndUsers
|
||||
}
|
||||
else {
|
||||
Get-MgDomain
|
||||
'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
|
||||
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
||||
return $globalAdmins
|
||||
}
|
||||
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 }
|
||||
if ($founde3Sku.Count -ne 0) {
|
||||
$allE3Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde3Sku.SkuId) )" -All
|
||||
return $allE3Users
|
||||
'1.2.1' {
|
||||
# Test-ManagedApprovedPublicGroups
|
||||
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
||||
return $allGroups
|
||||
}
|
||||
else {
|
||||
return $null
|
||||
'1.2.2' {
|
||||
# Test-BlockSharedMailboxSignIn.ps1
|
||||
$users = Get-MgUser
|
||||
return $users
|
||||
}
|
||||
'1.3.1' {
|
||||
# Test-PasswordNeverExpirePolicy.ps1
|
||||
$domains = if ($DomainName) {
|
||||
Get-MgDomain -DomainId $DomainName
|
||||
}
|
||||
else {
|
||||
Get-MgDomain
|
||||
}
|
||||
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 }
|
||||
if ($foundE3Sku.Count -ne 0) {
|
||||
$allE3Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($foundE3Sku.SkuId) )" -All
|
||||
return $allE3Users
|
||||
}
|
||||
else {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
'6.1.3' {
|
||||
# Test-MailboxAuditingE5
|
||||
$tenantSKUs = Get-MgSubscribedSku -All
|
||||
$e5SkuPartNumber = "SPE_E5"
|
||||
$foundE5Sku = $tenantSKUs | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber }
|
||||
if ($foundE5Sku.Count -ne 0) {
|
||||
$allE5Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($foundE5Sku.SkuId) )" -All
|
||||
return $allE5Users
|
||||
}
|
||||
else {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
'6.1.3' {
|
||||
# Test-MailboxAuditingE5
|
||||
$tenantSkus = Get-MgSubscribedSku -All
|
||||
$e5SkuPartNumber = "SPE_E5"
|
||||
$founde5Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber }
|
||||
if ($founde5Sku.Count -ne 0) {
|
||||
$allE5Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde5Sku.SkuId) )" -All
|
||||
return $allE5Users
|
||||
}
|
||||
else {
|
||||
return $null
|
||||
}
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
catch {
|
||||
throw "Get-CISMgOutput: `n$_"
|
||||
}
|
||||
}
|
||||
end {
|
||||
|
@@ -1,200 +1,161 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
This is a sample Private function only visible within the module.
|
||||
Retrieves configuration settings from SharePoint Online or PnP based on the specified recommendation.
|
||||
.DESCRIPTION
|
||||
This sample function is not exported to the module and only return the data passed as parameter.
|
||||
The Get-CISSpoOutput function retrieves specific configuration settings from SharePoint Online or PnP based on a recommendation number.
|
||||
It dynamically switches between using SPO and PnP commands based on the provided authentication context.
|
||||
.PARAMETER Rec
|
||||
The recommendation number corresponding to the specific test to be run.
|
||||
.INPUTS
|
||||
None. You cannot pipe objects to this function.
|
||||
.OUTPUTS
|
||||
PSCustomObject
|
||||
Returns configuration details for the specified recommendation.
|
||||
.EXAMPLE
|
||||
$null = Get-CISSpoOutput -PrivateData 'NOTHING TO SEE HERE'
|
||||
.PARAMETER PrivateData
|
||||
The PrivateData parameter is what will be returned without transformation.
|
||||
PS> Get-CISSpoOutput -Rec '7.2.1'
|
||||
Retrieves the LegacyAuthProtocolsEnabled property from the SharePoint Online or PnP tenant.
|
||||
#>
|
||||
function Get-CISSpoOutput {
|
||||
[cmdletBinding()]
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[Parameter(Mandatory = $true, HelpMessage = "The recommendation number corresponding to the specific test to be run.")]
|
||||
[String]$Rec
|
||||
)
|
||||
begin {
|
||||
# Begin Block #
|
||||
<#
|
||||
# Tests
|
||||
7.2.1
|
||||
7.2.2
|
||||
7.2.3
|
||||
7.2.4
|
||||
7.2.5
|
||||
7.2.6
|
||||
7.2.7
|
||||
7.2.9
|
||||
7.2.10
|
||||
7.3.1
|
||||
7.3.2
|
||||
7.3.4
|
||||
|
||||
# Test number array
|
||||
$testNumbers = @('7.2.1', '7.2.2', '7.2.3', '7.2.4', '7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.2.10', '7.3.1', '7.3.2', '7.3.4')
|
||||
#>
|
||||
# Check if PnP should be used
|
||||
$UsePnP = $script:PnpAuth
|
||||
# Determine the prefix based on the switch
|
||||
$prefix = if ($UsePnP) { "PnP" } else { "SPO" }
|
||||
# Define a hashtable to map the function calls
|
||||
$commandMap = @{
|
||||
# Test-ModernAuthSharePoint.ps1
|
||||
# 7.2.1 (L1) Ensure Legacy Authentication Protocols are disabled
|
||||
# $SPOTenant Mock Object
|
||||
'7.2.1' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property LegacyAuthProtocolsEnabled
|
||||
}
|
||||
# Test-SharePointAADB2B.ps1
|
||||
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
||||
# $SPOTenantAzureADB2B Mock Object
|
||||
'7.2.2' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property EnableAzureADB2BIntegration
|
||||
}
|
||||
# Test-RestrictExternalSharing.ps1
|
||||
# 7.2.3 (L1) Ensure external content sharing is restricted
|
||||
# Retrieve the SharingCapability setting for the SharePoint tenant
|
||||
# $SPOTenantSharingCapability Mock Object
|
||||
'7.2.3' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property SharingCapability
|
||||
}
|
||||
# Test-OneDriveContentRestrictions.ps1
|
||||
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||
# $SPOTenant Mock Object
|
||||
'7.2.4' = {
|
||||
Invoke-Command {
|
||||
if ($prefix -eq "SPO") {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)" | Select-Object -Property OneDriveSharingCapability
|
||||
} else {
|
||||
# Workaround until bugfix in PnP.PowerShell
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)" | Select-Object -Property OneDriveLoopSharingCapability | Select-Object @{Name = "OneDriveSharingCapability"; Expression = { $_.OneDriveLoopSharingCapability }}
|
||||
}
|
||||
}
|
||||
}
|
||||
# Test-SharePointGuestsItemSharing.ps1
|
||||
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
||||
# $SPOTenant Mock Object
|
||||
'7.2.5' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property PreventExternalUsersFromResharing
|
||||
}
|
||||
# Test-SharePointExternalSharingDomains.ps1
|
||||
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
||||
# Add Authorized Domains?
|
||||
# $SPOTenant Mock Object
|
||||
'7.2.6' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property SharingDomainRestrictionMode, SharingAllowedDomainList
|
||||
}
|
||||
# Test-LinkSharingRestrictions.ps1
|
||||
# Retrieve link sharing configuration for SharePoint and OneDrive
|
||||
# $SPOTenantLinkSharing Mock Object
|
||||
'7.2.7' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property DefaultSharingLinkType
|
||||
}
|
||||
# Test-GuestAccessExpiration.ps1
|
||||
# Retrieve SharePoint tenant settings related to guest access expiration
|
||||
# $SPOTenantGuestAccess Mock Object
|
||||
'7.2.9' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property ExternalUserExpirationRequired, ExternalUserExpireInDays
|
||||
}
|
||||
# Test-ReauthWithCode.ps1
|
||||
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||
# Retrieve reauthentication settings for SharePoint Online
|
||||
# $SPOTenantReauthentication Mock Object
|
||||
'7.2.10' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property EmailAttestationRequired, EmailAttestationReAuthDays
|
||||
}
|
||||
# Test-DisallowInfectedFilesDownload.ps1
|
||||
# Retrieve the SharePoint tenant configuration
|
||||
# $SPOTenantDisallowInfectedFileDownload Mock Object
|
||||
'7.3.1' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}Tenant").Name)"
|
||||
} | Select-Object -Property DisallowInfectedFileDownload
|
||||
}
|
||||
# Test-OneDriveSyncRestrictions.ps1
|
||||
# Retrieve OneDrive sync client restriction settings
|
||||
# Add isHybrid parameter?
|
||||
# $SPOTenantSyncClientRestriction Mock Object
|
||||
'7.3.2' = {
|
||||
Invoke-Command {
|
||||
& "$((Get-Command -Name "Get-${prefix}TenantSyncClientRestriction").Name)"
|
||||
} | Select-Object -Property TenantRestrictionEnabled, AllowedDomainList
|
||||
}
|
||||
# Test-RestrictCustomScripts.ps1
|
||||
# Retrieve all site collections and select necessary properties
|
||||
# $SPOSitesCustomScript Mock Object
|
||||
'7.3.4' = {
|
||||
Invoke-Command {
|
||||
if ($prefix -eq "SPO") {
|
||||
& "$((Get-Command -Name "Get-${prefix}Site").Name)" -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
||||
} else {
|
||||
& "$((Get-Command -Name "Get-${prefix}TenantSite").Name)" | Select-Object Title, Url, DenyAddAndCustomizePages
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process {
|
||||
Write-Verbose "Retuning data for Rec: $Rec"
|
||||
switch ($Rec) {
|
||||
'7.2.1' {
|
||||
# Test-ModernAuthSharePoint.ps1
|
||||
# $SPOTenant Mock Object
|
||||
<#
|
||||
$SPOTenant = [PSCustomObject]@{
|
||||
LegacyAuthProtocolsEnabled = $true
|
||||
}
|
||||
#>
|
||||
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
||||
return $SPOTenant
|
||||
try {
|
||||
Write-Verbose "Returning data for Rec: $Rec"
|
||||
if ($commandMap.ContainsKey($Rec)) {
|
||||
# Invoke the script block associated with the command
|
||||
$result = & $commandMap[$Rec] -ErrorAction Stop
|
||||
return $result
|
||||
}
|
||||
'7.2.2' {
|
||||
# Test-SharePointAADB2B.ps1
|
||||
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
||||
# $SPOTenantAzureADB2B Mock Object
|
||||
<#
|
||||
$SPOTenantAzureADB2B = [PSCustomObject]@{
|
||||
EnableAzureADB2BIntegration = $false
|
||||
}
|
||||
#>
|
||||
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
||||
return $SPOTenantAzureADB2B
|
||||
else {
|
||||
throw "No match found for test: $Rec"
|
||||
}
|
||||
'7.2.3' {
|
||||
# Test-RestrictExternalSharing.ps1
|
||||
# 7.2.3 (L1) Ensure external content sharing is restricted
|
||||
# Retrieve the SharingCapability setting for the SharePoint tenant
|
||||
# $SPOTenantSharingCapability Mock Object
|
||||
<#
|
||||
$SPOTenantSharingCapability = [PSCustomObject]@{
|
||||
SharingCapability = "ExternalUserAndGuestSharing"
|
||||
}
|
||||
#>
|
||||
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
||||
return $SPOTenantSharingCapability
|
||||
}
|
||||
'7.2.4' {
|
||||
# Test-OneDriveContentRestrictions.ps1
|
||||
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||
# $SPOTenant Mock Object
|
||||
<#
|
||||
$SPOTenant = [PSCustomObject]@{
|
||||
OneDriveSharingCapability = "ExternalUserAndGuestSharing"
|
||||
}
|
||||
#>
|
||||
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
||||
return $SPOTenant
|
||||
}
|
||||
'7.2.5' {
|
||||
# Test-SharePointGuestsItemSharing.ps1
|
||||
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
||||
# $SPOTenant Mock Object
|
||||
<#
|
||||
$SPOTenant = [PSCustomObject]@{
|
||||
PreventExternalUsersFromResharing = $false
|
||||
}
|
||||
#>
|
||||
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
|
||||
return $SPOTenant
|
||||
}
|
||||
'7.2.6' {
|
||||
# Test-SharePointExternalSharingDomains.ps1
|
||||
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
||||
# Add Authorized Domains?
|
||||
# $SPOTenant Mock Object
|
||||
<#
|
||||
$SPOTenant = [PSCustomObject]@{
|
||||
SharingDomainRestrictionMode = "AllowList"
|
||||
SharingAllowedDomainList = "domain1.com", "domain2.com"
|
||||
}
|
||||
#>
|
||||
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
|
||||
return $SPOTenant
|
||||
}
|
||||
'7.2.7' {
|
||||
# Test-LinkSharingRestrictions.ps1
|
||||
# Retrieve link sharing configuration for SharePoint and OneDrive
|
||||
# $SPOTenantLinkSharing Mock Object
|
||||
<#
|
||||
$$SPOTenantLinkSharing = [PSCustomObject]@{
|
||||
DefaultSharingLinkType = "Direct"
|
||||
}
|
||||
#>
|
||||
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
||||
return $SPOTenantLinkSharing
|
||||
}
|
||||
'7.2.9' {
|
||||
# Test-GuestAccessExpiration.ps1
|
||||
# Retrieve SharePoint tenant settings related to guest access expiration
|
||||
# $SPOTenantGuestAccess Mock Object
|
||||
<#
|
||||
$SPOTenantGuestAccess = [PSCustomObject]@{
|
||||
ExternalUserExpirationRequired = "$false"
|
||||
ExternalUserExpireInDays = "60"
|
||||
}
|
||||
#>
|
||||
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
|
||||
return $SPOTenantGuestAccess
|
||||
}
|
||||
'7.2.10' {
|
||||
# Test-ReauthWithCode.ps1
|
||||
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||
# Retrieve reauthentication settings for SharePoint Online
|
||||
# $SPOTenantReauthentication Mock Object
|
||||
<#
|
||||
$SPOTenantReauthentication = [PSCustomObject]@{
|
||||
EmailAttestationRequired = "$false"
|
||||
EmailAttestationReAuthDays = "30"
|
||||
}
|
||||
#>
|
||||
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
||||
return $SPOTenantReauthentication
|
||||
}
|
||||
'7.3.1' {
|
||||
# Test-DisallowInfectedFilesDownload.ps1
|
||||
# Retrieve the SharePoint tenant configuration
|
||||
# $SPOTenantDisallowInfectedFileDownload Mock Object
|
||||
<#
|
||||
$SPOTenantDisallowInfectedFileDownload = [PSCustomObject]@{
|
||||
DisallowInfectedFileDownload = $false
|
||||
}
|
||||
#>
|
||||
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
||||
return $SPOTenantDisallowInfectedFileDownload
|
||||
}
|
||||
'7.3.2' {
|
||||
# Test-OneDriveSyncRestrictions.ps1
|
||||
# Retrieve OneDrive sync client restriction settings
|
||||
# Add isHybrid paramter?
|
||||
# $SPOTenantSyncClientRestriction Mock Object
|
||||
<#
|
||||
$SPOTenantSyncClientRestriction = [PSCustomObject]@{
|
||||
TenantRestrictionEnabled = $true
|
||||
AllowedDomainList = "786548DD-877B-4760-A749-6B1EFBC1190A", "877564FF-877B-4760-A749-6B1EFBC1190A"
|
||||
}
|
||||
#>
|
||||
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
||||
return $SPOTenantSyncClientRestriction
|
||||
}
|
||||
'7.3.4' {
|
||||
# Test-RestrictCustomScripts.ps1
|
||||
# Retrieve all site collections and select necessary properties
|
||||
# $SPOSitesCustomScript Mock Object
|
||||
<#
|
||||
$SPOSitesCustomScript = [PSCustomObject]@{
|
||||
Title = "Site Collection 1"
|
||||
Url = "https://contoso.sharepoint.com/sites/site1"
|
||||
DenyAddAndCustomizePages = "Enabled"
|
||||
}
|
||||
#>
|
||||
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
||||
return $SPOSitesCustomScript
|
||||
}
|
||||
default { throw "No match found for test: $Rec" }
|
||||
}
|
||||
catch {
|
||||
throw "Get-CISSpoOutput: `n$_"
|
||||
}
|
||||
}
|
||||
end {
|
||||
Write-Verbose "Retuning data for Rec: $Rec"
|
||||
Write-Verbose "Finished processing for Rec: $Rec"
|
||||
}
|
||||
} # end function Get-CISMSTeamsOutput
|
||||
}
|
||||
|
@@ -4,20 +4,27 @@ function Get-RequiredModule {
|
||||
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"; SubModules = @() },
|
||||
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() },
|
||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
|
||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||
)
|
||||
if (($script:PnpAuth)) {
|
||||
return @(
|
||||
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
|
||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||
@{ ModuleName = "PnP.PowerShell"; RequiredVersion = "2.5.0"; SubModules = @() },
|
||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||
)
|
||||
}
|
||||
else {
|
||||
return @(
|
||||
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
|
||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
|
||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||
)
|
||||
}
|
||||
}
|
||||
'SyncFunction' {
|
||||
return @(
|
||||
|
@@ -27,7 +27,7 @@ function Get-TestError {
|
||||
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $LastError })
|
||||
# Call Initialize-CISAuditResult with error parameters
|
||||
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||
Write-Verbose "An error occurred during the test: `n$LastError" -Verbose
|
||||
Write-Verbose "An error occurred during the test $recnum`: `n$LastError" -Verbose
|
||||
return $auditResult
|
||||
}
|
||||
|
||||
|
@@ -8,10 +8,7 @@ function Get-UniqueConnection {
|
||||
|
||||
$uniqueConnections = @()
|
||||
|
||||
if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||
$uniqueConnections += "AzureAD"
|
||||
}
|
||||
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph" -or $Connections -contains "EXO | Microsoft Graph") {
|
||||
$uniqueConnections += "Microsoft Graph"
|
||||
}
|
||||
if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||
|
@@ -33,7 +33,7 @@ function Invoke-TestFunction {
|
||||
return $result
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
$script:FailedTests.Add([PSCustomObject]@{ Test = $functionName; Error = $_ })
|
||||
|
||||
# Call Initialize-CISAuditResult with error parameters
|
||||
|
@@ -18,15 +18,15 @@ function Measure-AuditResult {
|
||||
$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%." -ForegroundColor Magenta
|
||||
Write-Information "Audit completed. $passedTests out of $totalTests tests passed."
|
||||
Write-Information "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
|
||||
Write-Verbose "The following tests failed to complete:"
|
||||
foreach ($failedTest in $FailedTests) {
|
||||
Write-Host "Test: $($failedTest.Test)" -ForegroundColor Yellow
|
||||
Write-Host "Error: $($failedTest.Error)" -ForegroundColor Yellow
|
||||
Write-Verbose "Test: $($failedTest.Test)"
|
||||
Write-Verbose "Error: $($failedTest.Error)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@
|
||||
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". This parameter is used to output a specific test result.
|
||||
.PARAMETER ExportAllTests
|
||||
.PARAMETER ExportNestedTables
|
||||
Switch to export all test results. When specified, all test results are exported to the specified path.
|
||||
.PARAMETER ExportPath
|
||||
The path where the CSV or Excel files will be exported. This parameter is mandatory when exporting all tests.
|
||||
@@ -28,28 +28,28 @@
|
||||
Export-M365SecurityAuditTable -AuditResults $object -OutputTestNumber 6.1.2
|
||||
# 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-M365SecurityAuditTable -ExportNestedTables -AuditResults $object -ExportPath "C:\temp"
|
||||
# Exports all audit results to the specified path in CSV format.
|
||||
.EXAMPLE
|
||||
Export-M365SecurityAuditTable -CsvPath "C:\temp\auditresultstoday1.csv" -OutputTestNumber 6.1.2
|
||||
# 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-M365SecurityAuditTable -ExportNestedTables -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp"
|
||||
# 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-M365SecurityAuditTable -ExportNestedTables -AuditResults $object -ExportPath "C:\temp" -ExportOriginalTests
|
||||
# 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-M365SecurityAuditTable -ExportNestedTables -CsvPath "C:\temp\auditresultstoday1.csv" -ExportPath "C:\temp" -ExportOriginalTests
|
||||
# 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
|
||||
Export-M365SecurityAuditTable -ExportNestedTables -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
|
||||
#>
|
||||
function Export-M365SecurityAuditTable {
|
||||
[CmdletBinding()]
|
||||
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
|
||||
[OutputType([PSCustomObject])]
|
||||
param (
|
||||
[Parameter(Mandatory = $true, Position = 1, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
@@ -65,21 +65,30 @@ function Export-M365SecurityAuditTable {
|
||||
[string]$OutputTestNumber,
|
||||
[Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
[Parameter(Mandatory = $false, Position = 0, ParameterSetName = "ExportAllResultsFromCsv")]
|
||||
[switch]$ExportAllTests,
|
||||
[switch]$ExportNestedTables,
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")]
|
||||
[string]$ExportPath,
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "ExportAllResultsFromCsv")]
|
||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
|
||||
[switch]$ExportOriginalTests,
|
||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
|
||||
[switch]$ExportToExcel
|
||||
[switch]$ExportToExcel,
|
||||
# Add Prefix to filename after date when outputting to excel or csv.
|
||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromAuditResults")]
|
||||
[Parameter(Mandatory = $false, ParameterSetName = "ExportAllResultsFromCsv")]
|
||||
# Validate that the count of letters in the prefix is less than 5.
|
||||
[ValidateLength(0, 5)]
|
||||
[string]$Prefix = "Corp"
|
||||
)
|
||||
Begin {
|
||||
$createdFiles = @() # Initialize an array to keep track of created files
|
||||
|
||||
if ($ExportToExcel) {
|
||||
Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion "7.8.9"
|
||||
if ($PSCmdlet.ShouldProcess("ImportExcel v7.8.9", "Assert-ModuleAvailability")) {
|
||||
Assert-ModuleAvailability -ModuleName ImportExcel -RequiredVersion "7.8.9"
|
||||
}
|
||||
}
|
||||
if ($PSCmdlet.ParameterSetName -like "ExportAllResultsFromCsv" -or $PSCmdlet.ParameterSetName -eq "OutputObjectFromCsvSingle") {
|
||||
$AuditResults = Import-Csv -Path $CsvPath | ForEach-Object {
|
||||
@@ -93,7 +102,7 @@ function Export-M365SecurityAuditTable {
|
||||
Initialize-CISAuditResult @params
|
||||
}
|
||||
}
|
||||
if ($ExportAllTests) {
|
||||
if ($ExportNestedTables) {
|
||||
$TestNumbers = "1.1.1", "1.3.1", "6.1.2", "6.1.3", "7.3.4"
|
||||
}
|
||||
$results = @()
|
||||
@@ -127,94 +136,92 @@ 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) {
|
||||
$fileName = "$ExportPath\$($timestamp)_$($result.TestNumber).$($testDef.TestFileName -replace '\.ps1$').csv"
|
||||
if ($result.Details.Count -eq 0) {
|
||||
Write-Information "No results found for test number $($result.TestNumber)." -InformationAction Continue
|
||||
}
|
||||
else {
|
||||
if (($result.Details -ne "No M365 E3 licenses found.") -and ($result.Details -ne "No M365 E5 licenses found.")) {
|
||||
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
|
||||
if ($PSCmdlet.ShouldProcess("Export-M365SecurityAuditTable", "Exporting results to $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) {
|
||||
$fileName = "$ExportPath\$($timestamp)_$($result.TestNumber).$($testDef.TestFileName -replace '\.ps1$').csv"
|
||||
if ($result.Details.Count -eq 0) {
|
||||
Write-Information "No results found for test number $($result.TestNumber)."
|
||||
}
|
||||
else {
|
||||
if (($result.Details -ne "No M365 E3 licenses found.") -and ($result.Details -ne "No M365 E5 licenses found.")) {
|
||||
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
|
||||
}
|
||||
else {
|
||||
$result.Details | Export-Csv -Path $fileName -NoTypeInformation
|
||||
$createdFiles += $fileName # Add the created file to the array
|
||||
}
|
||||
$exportedTests += $result.TestNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($exportedTests.Count -gt 0) {
|
||||
Write-Information "The following tests were exported: $($exportedTests -join ', ')" -InformationAction Continue
|
||||
}
|
||||
else {
|
||||
if ($exportedTests.Count -gt 0) {
|
||||
Write-Information "The following tests were exported: $($exportedTests -join ', ')"
|
||||
}
|
||||
else {
|
||||
if ($ExportOriginalTests) {
|
||||
Write-Information "Full audit results exported however, none of the following tests had exports: `n1.1.1, 1.3.1, 6.1.2, 6.1.3, 7.3.4"
|
||||
}
|
||||
else {
|
||||
Write-Information "No specified tests were included in the export."
|
||||
}
|
||||
}
|
||||
if ($ExportOriginalTests) {
|
||||
Write-Information "Full audit results exported however, none of the following tests had exports: `n1.1.1, 1.3.1, 6.1.2, 6.1.3, 7.3.4" -InformationAction Continue
|
||||
# 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`_$Prefix-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
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Information "No specified tests were included in the export." -InformationAction Continue
|
||||
# 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)"
|
||||
}
|
||||
}
|
||||
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
|
||||
$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`_$Prefix-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
|
||||
}
|
||||
else {
|
||||
$updatedAuditResults | Export-Csv -Path $originalFileName -NoTypeInformation
|
||||
$createdFiles += $originalFileName # Add the created file to the array
|
||||
# Compute the hash for the zip file and rename it
|
||||
$zipHash = Get-FileHash -Path $zipFilePath -Algorithm SHA256
|
||||
$newZipFilePath = "$ExportPath\$timestamp`_$Prefix-M365FoundationsAudit_$($zipHash.Hash.Substring(0, 8)).zip"
|
||||
Rename-Item -Path $zipFilePath -NewName $newZipFilePath
|
||||
# Output the zip file path with hash
|
||||
return [PSCustomObject]@{
|
||||
ZipFilePath = $newZipFilePath
|
||||
}
|
||||
}
|
||||
# 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
|
||||
}
|
||||
else {
|
||||
Write-Information "No results found for test number $($OutputTestNumber)." -InformationAction Continue
|
||||
Write-Information "No results found for test number $($OutputTestNumber)."
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@@ -37,91 +37,42 @@
|
||||
If specified, the cmdlet will not check for the presence of required modules.
|
||||
.PARAMETER DoNotConfirmConnections
|
||||
If specified, the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.
|
||||
.PARAMETER AuthParams
|
||||
Specifies an authentication object containing parameters for application-based authentication. If provided, this will be used for connecting to services.
|
||||
.EXAMPLE
|
||||
PS> Invoke-M365SecurityAudit
|
||||
|
||||
Performs a security audit using default parameters.
|
||||
Output:
|
||||
|
||||
Status : Fail
|
||||
ELevel : E3
|
||||
ProfileLevel: L1
|
||||
Connection : Microsoft Graph
|
||||
Rec : 1.1.1
|
||||
Result : False
|
||||
Details : Non-compliant accounts:
|
||||
Username | Roles | HybridStatus | Missing Licence
|
||||
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||
FailureReason: Non-Compliant Accounts: 2
|
||||
# Performs a security audit using default parameters.
|
||||
.EXAMPLE
|
||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
||||
|
||||
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
||||
Output:
|
||||
|
||||
Status : Fail
|
||||
ELevel : E5
|
||||
ProfileLevel: L1
|
||||
Connection : Microsoft Graph
|
||||
Rec : 1.1.1
|
||||
Result : False
|
||||
Details : Non-compliant accounts:
|
||||
Username | Roles | HybridStatus | Missing Licence
|
||||
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||
FailureReason: Non-Compliant Accounts: 2
|
||||
# Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
||||
.EXAMPLE
|
||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
|
||||
|
||||
Performs an audit including all tests where IG1 is true.
|
||||
Output:
|
||||
|
||||
Status : Fail
|
||||
ELevel : E3
|
||||
ProfileLevel: L1
|
||||
Connection : Microsoft Graph
|
||||
Rec : 1.1.1
|
||||
Result : False
|
||||
Details : Non-compliant accounts:
|
||||
Username | Roles | HybridStatus | Missing Licence
|
||||
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||
FailureReason: Non-Compliant Accounts: 2
|
||||
# Performs a security audit while including tests where IG1 is true.
|
||||
.EXAMPLE
|
||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
||||
|
||||
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
||||
Output:
|
||||
|
||||
Status : Fail
|
||||
ELevel : E3
|
||||
ProfileLevel: L1
|
||||
Connection : Microsoft Graph
|
||||
Rec : 1.1.1
|
||||
Result : False
|
||||
Details : Non-compliant accounts:
|
||||
Username | Roles | HybridStatus | Missing Licence
|
||||
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||
FailureReason: Non-Compliant Accounts: 2
|
||||
# Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
||||
.EXAMPLE
|
||||
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
||||
PS> Export-M365SecurityAuditTable -AuditResults $auditResults -ExportPath "C:\temp" -ExportOriginalTests -ExportAllTests
|
||||
|
||||
Or:
|
||||
.EXAMPLE
|
||||
# (PowerShell 7.x Only) Creating a new authentication object for the security audit for app-based authentication.
|
||||
PS> $authParams = New-M365SecurityAuditAuthObject `
|
||||
-ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" `
|
||||
-ClientId "12345678-1234-1234-1234-123456789012" `
|
||||
-TenantId "12345678-1234-1234-1234-123456789012" `
|
||||
-OnMicrosoftUrl "yourcompany.onmicrosoft.com" `
|
||||
-SpAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
Invoke-M365SecurityAudit -AuthParams $authParams -TenantAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
# Or:
|
||||
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
||||
|
||||
Captures the audit results into a variable and exports them to a CSV file (Nested tables will be truncated).
|
||||
# Captures the audit results into a variable and exports them to a CSV file (Nested tables will be truncated).
|
||||
Output:
|
||||
CISAuditResult[]
|
||||
auditResults.csv
|
||||
.EXAMPLE
|
||||
PS> Invoke-M365SecurityAudit -WhatIf
|
||||
|
||||
Displays what would happen if the cmdlet is run without actually performing the audit.
|
||||
Output:
|
||||
|
||||
What if: Performing the operation "Invoke-M365SecurityAudit" on target "Microsoft 365 environment".
|
||||
.INPUTS
|
||||
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
||||
@@ -139,7 +90,8 @@
|
||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||
#>
|
||||
function Invoke-M365SecurityAudit {
|
||||
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
||||
# Add confirm to high
|
||||
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High" , DefaultParameterSetName = 'Default')]
|
||||
[OutputType([CISAuditResult[]])]
|
||||
param (
|
||||
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
|
||||
@@ -202,20 +154,25 @@ function Invoke-M365SecurityAudit {
|
||||
[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
|
||||
[switch]$DoNotConfirmConnections,
|
||||
[Parameter(Mandatory = $false, HelpMessage = "Specifies an authentication object containing parameters for application-based authentication.")]
|
||||
[CISAuthenticationParameters]$AuthParams
|
||||
)
|
||||
Begin {
|
||||
if ($script:MaximumFunctionCount -lt 8192) {
|
||||
Write-Verbose "Setting the `$script:MaximumFunctionCount to 8192 for the test run." -Verbose
|
||||
Write-Verbose "Setting the `$script:MaximumFunctionCount to 8192 for the test run."
|
||||
$script:MaximumFunctionCount = 8192
|
||||
}
|
||||
if ($AuthParams) {
|
||||
$script:PnpAuth = $true
|
||||
}
|
||||
# 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
|
||||
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Modules: $requiredModulesFormatted", "Assert-ModuleAvailability")) {
|
||||
Write-Information "Checking for and installing required modules..."
|
||||
foreach ($module in $requiredModules) {
|
||||
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
|
||||
}
|
||||
@@ -265,46 +222,59 @@ function Invoke-M365SecurityAudit {
|
||||
try {
|
||||
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
||||
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
|
||||
Write-Host "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" -ForegroundColor DarkMagenta
|
||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections -SkipConfirmation:$DoNotConfirmConnections
|
||||
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')"
|
||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections -SkipConfirmation:$DoNotConfirmConnections -AuthParams $AuthParams
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Host "Connection execution aborted: $_" -ForegroundColor Red
|
||||
break
|
||||
Throw "Connection execution aborted: $_"
|
||||
}
|
||||
try {
|
||||
Write-Host "A total of $($totalTests) tests were selected to run..." -ForegroundColor DarkMagenta
|
||||
# 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
|
||||
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
|
||||
Write-Information "A total of $($totalTests) tests were selected to run..."
|
||||
# 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-Verbose "Failed to load test function $($_.Name): $_"
|
||||
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
|
||||
}
|
||||
}
|
||||
Catch {
|
||||
# Log the error and add the test to the failed tests collection
|
||||
Write-Verbose "Failed to load test function $($_.Name): $_" -Verbose
|
||||
$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
|
||||
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
|
||||
$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
|
||||
Write-Information "Executing test function: $functionName"
|
||||
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $DomainName -ApprovedCloudStorageProviders $ApprovedCloudStorageProviders -ApprovedFederatedDomains $ApprovedFederatedDomains
|
||||
# Add the result to the collection
|
||||
[void]$allAuditResults.Add($auditResult)
|
||||
}
|
||||
# Call the private function to calculate and display results
|
||||
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
||||
# 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) {
|
||||
Write-Information "The following tests exceeded the details length limit: $($exceedingTests -join ', ')"
|
||||
Write-Information "( Assuming the results were instantiated. Ex: `$object = invoke-M365SecurityAudit )`nUse the following command and adjust as necessary to view the full details of the test results:"
|
||||
Write-Information "Export-M365SecurityAuditTable -ExportAllTests -AuditResults `$object -ExportPath `"C:\temp`" -ExportOriginalTests"
|
||||
}
|
||||
return $allAuditResults.ToArray() | Sort-Object -Property Rec
|
||||
}
|
||||
}
|
||||
catch {
|
||||
# Log the error and add the test to the failed tests collection
|
||||
Write-Verbose "Invoke-M365SecurityAudit: Failed to load test function $($_.Name): $_" -Verbose
|
||||
throw "Failed to execute test function $($testFunction.Name): $_"
|
||||
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
|
||||
}
|
||||
finally {
|
||||
@@ -315,20 +285,6 @@ function Invoke-M365SecurityAudit {
|
||||
}
|
||||
}
|
||||
End {
|
||||
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
|
||||
# Call the private function to calculate and display results
|
||||
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
||||
# Return all collected audit results
|
||||
# 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) {
|
||||
Write-Information "The following tests exceeded the details length limit: $($exceedingTests -join ', ')" -InformationAction Continue
|
||||
Write-Host "(Assuming the results were instantiated. Ex: `$object = invoke-M365SecurityAudit) Use the following command and adjust as neccesary to view the full details of the test results:" -ForegroundColor DarkCyan
|
||||
Write-Host "Export-M365SecurityAuditTable -ExportAllTests -AuditResults `$object -ExportPath `"C:\temp`" -ExportOriginalTests" -ForegroundColor Green
|
||||
}
|
||||
return $allAuditResults.ToArray() | Sort-Object -Property Rec
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
source/Public/New-M365SecurityAuditAuthObject.ps1
Normal file
64
source/Public/New-M365SecurityAuditAuthObject.ps1
Normal file
@@ -0,0 +1,64 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Creates a new CISAuthenticationParameters object for Microsoft 365 authentication.
|
||||
.DESCRIPTION
|
||||
The New-M365SecurityAuditAuthObject function constructs a new CISAuthenticationParameters object
|
||||
containing the necessary credentials and URLs for authenticating to various Microsoft 365 services.
|
||||
It validates input parameters to ensure they conform to expected formats and length requirements.
|
||||
An app registration in Azure AD with the required permissions to EXO, SPO, MSTeams and MgGraph is needed.
|
||||
.PARAMETER ClientCertThumbPrint
|
||||
The thumbprint of the client certificate used for authentication. It must be a 40-character hexadecimal string.
|
||||
This certificate is used to authenticate the application in Azure AD.
|
||||
.PARAMETER ClientId
|
||||
The Client ID (Application ID) of the Azure AD application. It must be a valid GUID format.
|
||||
.PARAMETER TenantId
|
||||
The Tenant ID of the Azure AD directory. It must be a valid GUID format representing your Microsoft 365 tenant.
|
||||
.PARAMETER OnMicrosoftUrl
|
||||
The URL of your onmicrosoft.com domain. It should be in the format 'example.onmicrosoft.com'.
|
||||
.PARAMETER SpAdminUrl
|
||||
The SharePoint admin URL, which should end with '-admin.sharepoint.com'. This URL is used for connecting to SharePoint Online.
|
||||
.INPUTS
|
||||
None. You cannot pipe objects to this function.
|
||||
.OUTPUTS
|
||||
CISAuthenticationParameters
|
||||
The function returns an instance of the CISAuthenticationParameters class containing the authentication details.
|
||||
.EXAMPLE
|
||||
PS> $authParams = New-M365SecurityAuditAuthObject -ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" `
|
||||
-ClientId "12345678-1234-1234-1234-123456789012" `
|
||||
-TenantId "12345678-1234-1234-1234-123456789012" `
|
||||
-OnMicrosoftUrl "yourcompany.onmicrosoft.com" `
|
||||
-SpAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
Creates a new CISAuthenticationParameters object with the specified credentials and URLs, validating each parameter's format and length.
|
||||
.NOTES
|
||||
Requires PowerShell 7.0 or later.
|
||||
#>
|
||||
function New-M365SecurityAuditAuthObject {
|
||||
[CmdletBinding()]
|
||||
[OutputType([CISAuthenticationParameters])]
|
||||
param(
|
||||
[Parameter(Mandatory = $true, HelpMessage = "The 40-character hexadecimal thumbprint of the client certificate.")]
|
||||
[ValidatePattern("^[0-9a-fA-F]{40}$")] # Regex for a valid thumbprint format
|
||||
[ValidateLength(40, 40)] # Enforce exact length
|
||||
[string]$ClientCertThumbPrint,
|
||||
[Parameter(Mandatory = $true, HelpMessage = "The Client ID (GUID format) of the Azure AD application.")]
|
||||
[ValidatePattern("^[0-9a-fA-F\-]{36}$")] # Regex for a valid GUID
|
||||
[string]$ClientId,
|
||||
[Parameter(Mandatory = $true, HelpMessage = "The Tenant ID (GUID format) of the Azure AD directory.")]
|
||||
[ValidatePattern("^[0-9a-fA-F\-]{36}$")] # Regex for a valid GUID
|
||||
[string]$TenantId,
|
||||
[Parameter(Mandatory = $true, HelpMessage = "The onmicrosoft.com domain URL (e.g., 'example.onmicrosoft.com').")]
|
||||
[ValidatePattern("^[a-zA-Z0-9]+\.onmicrosoft\.com$")] # Regex for a valid onmicrosoft.com URL
|
||||
[string]$OnMicrosoftUrl,
|
||||
[Parameter(Mandatory = $true, HelpMessage = "The SharePoint admin URL ending with '-admin.sharepoint.com'.")]
|
||||
[ValidatePattern("^https:\/\/[a-zA-Z0-9\-]+\-admin\.sharepoint\.com$")] # Regex for a valid SharePoint admin URL
|
||||
[string]$SpAdminUrl
|
||||
)
|
||||
# Create and return the authentication parameters object
|
||||
return [CISAuthenticationParameters]::new(
|
||||
$ClientCertThumbPrint,
|
||||
$ClientId,
|
||||
$TenantId,
|
||||
$OnMicrosoftUrl,
|
||||
$SpAdminUrl
|
||||
)
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -50,6 +50,14 @@ EXAMPLES
|
||||
|
||||
# Example 7: Granting Microsoft Graph permissions to the auditor
|
||||
Grant-M365SecurityAuditConsent -UserPrincipalNameForConsent 'user@example.com'
|
||||
|
||||
# Example 8: (PowerShell 7.x Only) Creating a new authentication object for the security audit for app-based authentication.
|
||||
$authParams = New-M365SecurityAuditAuthObject -ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" `
|
||||
-ClientId "12345678-1234-1234-1234-123456789012" `
|
||||
-TenantId "12345678-1234-1234-1234-123456789012" `
|
||||
-OnMicrosoftUrl "yourcompany.onmicrosoft.com" `
|
||||
-SpAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
Invoke-M365SecurityAudit -AuthParams $authParams -TenantAdminUrl "https://yourcompany-admin.sharepoint.com"
|
||||
|
||||
NOTE
|
||||
Ensure that you have the necessary permissions and administrative roles in
|
||||
|
@@ -2,7 +2,7 @@
|
||||
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
|
||||
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,EXO | Microsoft Graph
|
||||
5,Test-PasswordNeverExpirePolicy.ps1,1.3.1,Ensure the 'Password expiration policy' is set to 'Set passwords to never expire',E3,L1,5.2,Use Unique Passwords,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||
6,Test-ExternalSharingCalendars.ps1,1.3.3,Ensure 'External sharing' of calendars is not available,E3,L2,4.8,Uninstall or Disable Unnecessary Services on Enterprise Assets and Software,FALSE,TRUE,TRUE,TRUE,EXO
|
||||
7,Test-CustomerLockbox.ps1,1.3.6,Ensure the customer lockbox feature is enabled,E5,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||
|
|
@@ -215,7 +215,7 @@ function Test-AntiPhishingPolicy {
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
$auditResult = Get-TestError -LastError $_ -recnum $recnum
|
||||
}
|
||||
}
|
||||
|
@@ -58,9 +58,9 @@ function Test-BlockSharedMailboxSignIn {
|
||||
}
|
||||
)
|
||||
#>
|
||||
$users = Get-CISAadOutput -Rec $recnum
|
||||
$users = Get-CISMgOutput -Rec $recnum
|
||||
# Step: Retrieve details of shared mailboxes from Azure AD (Condition B: Pass/Fail)
|
||||
$sharedMailboxDetails = $users | Where-Object {$_.objectid -in $objectids}
|
||||
$sharedMailboxDetails = $users | Where-Object {$_.id -in $objectids}
|
||||
# Step: Identify enabled mailboxes (Condition B: Pass/Fail)
|
||||
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
||||
$allBlocked = $enabledMailboxes.Count -eq 0
|
||||
|
@@ -101,7 +101,7 @@ function Test-MailboxAuditingE3 {
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
# Retrieve the description from the test definitions
|
||||
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||
|
@@ -103,7 +103,7 @@ function Test-MailboxAuditingE5 {
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
# Retrieve the description from the test definitions
|
||||
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||
|
@@ -90,7 +90,7 @@ function Test-SafeAttachmentsPolicy {
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
# Retrieve the description from the test definitions
|
||||
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||
|
@@ -88,7 +88,7 @@ AllowSafeDocsOpen: $($_.AllowSafeDocsOpen)
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
# Retrieve the description from the test definitions
|
||||
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||
|
@@ -58,7 +58,7 @@ function Test-SafeLinksOfficeApps {
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
# Retrieve the description from the test definitions
|
||||
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||
|
@@ -108,7 +108,7 @@ function Test-SpamPolicyAdminNotify {
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
catch {
|
||||
Write-Error "An error occurred during the test: $_"
|
||||
Write-Error "An error occurred during the test $recnum`:: $_"
|
||||
$auditResult = Get-TestError -LastError $_ -recnum $recnum
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user