Compare commits
177 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4db0fd3742 | ||
|
83a8e31aa5 | ||
|
b9de0638bb | ||
|
5a0475c253 | ||
|
312aabc81c | ||
|
e6da6d9d47 | ||
|
014c42b3fe | ||
|
fbfb5b5986 | ||
|
03b5bb47e2 | ||
|
9dc99636d3 | ||
|
afe657ffc0 | ||
|
702f557579 | ||
|
f855ef7d0b | ||
|
270e980a57 | ||
|
ff90669984 | ||
|
f2e799af2f | ||
|
4a4d200197 | ||
|
9199d97fc2 | ||
|
5d681f3d72 | ||
|
f926c63533 | ||
|
d5044f0bf4 | ||
|
055ab42261 | ||
|
0d97b95c6c | ||
|
c185878674 | ||
|
61063ee63c | ||
|
4115f1e83e | ||
|
0b3213d957 | ||
|
9d9b9e70d9 | ||
|
4167a37121 | ||
|
66536e34a7 | ||
|
db9b206ae3 | ||
|
8a9044486b | ||
|
447be9cacb | ||
|
71c798c52a | ||
|
d0270027f9 | ||
|
0569fd98cc | ||
|
c842ae9720 | ||
|
63b9e8b75d | ||
|
f7b87ebc78 | ||
|
b70da1845f | ||
|
ef55447e67 | ||
|
7a9d2885f3 | ||
|
4ab5affc9f | ||
|
5871294210 | ||
|
a5dc7f1ebd | ||
|
c0222ef3bc | ||
|
83ee6c2ac3 | ||
|
cbdb31c7c5 | ||
|
a5d26917d3 | ||
|
4f8df29c72 | ||
|
bb1df11128 | ||
|
2d9a1a1d10 | ||
|
41dbf2f0db | ||
|
70dcd74643 | ||
|
f6aa4b83dd | ||
|
d58d0b664d | ||
|
b9a8a75945 | ||
|
6495073a10 | ||
|
e0e2a04b6a | ||
|
d85968935b | ||
|
f47efa74f9 | ||
|
f905f269d1 | ||
|
8719900af7 | ||
|
8922ea12cd | ||
|
c6bdad0477 | ||
|
85bd0fb19f | ||
|
8de61dda9f | ||
|
c530d2df76 | ||
|
f5ab68dd63 | ||
|
d9ed3b60b7 | ||
|
4857aead5e | ||
|
92e5952f7e | ||
|
cb52ce1a73 | ||
|
4bc1f8fdda | ||
|
8446d101a7 | ||
|
06aeadaee5 | ||
|
a9e44a7c6f | ||
|
2712f78412 | ||
|
df89e23bc1 | ||
|
2027e8b21b | ||
|
5c60f39dad | ||
|
399288b10a | ||
|
87d0aaaea6 | ||
|
8301b8cdbe | ||
|
3f4166e98a | ||
|
7582511dd5 | ||
|
20afb8d83e | ||
|
c378f5d119 | ||
|
3a37d465e8 | ||
|
531cedc0f6 | ||
|
54f9e086a9 | ||
|
d1555e1d25 | ||
|
ac8014ae77 | ||
|
fc9923b980 | ||
|
290540b25a | ||
|
b064f9f271 | ||
|
cafcaf2671 | ||
|
129bb33a99 | ||
|
8505439516 | ||
|
686272d4e0 | ||
|
f445893aed | ||
|
050ea83acd | ||
|
deec4c4f5e | ||
|
6930673209 | ||
|
87db439d66 | ||
|
3d84a86793 | ||
|
dbc577bc67 | ||
|
d1a5cb8d73 | ||
|
c1e94ff3bc | ||
|
532cb942e8 | ||
|
2d93422027 | ||
|
75faf04ea6 | ||
|
c7cdaa4bf6 | ||
|
d511ea7b27 | ||
|
5b3c68a8f9 | ||
|
3883e33a4a | ||
|
398ce397f5 | ||
|
776b0bf2ec | ||
|
8d03d1825b | ||
|
26fa3a8922 | ||
|
c918f0203e | ||
|
c752b7e4fd | ||
|
3cf76bb6b3 | ||
|
36cb3f1944 | ||
|
c122174cb7 | ||
|
d5c64910ab | ||
|
77c74432fe | ||
|
b4c0993240 | ||
|
e96a5a9d48 | ||
|
2148a37b35 | ||
|
0196607f69 | ||
|
f5a3f0e460 | ||
|
5ed1e1de35 | ||
|
0d049c6dcb | ||
|
4b28258b3e | ||
|
b8690ddb44 | ||
|
dc88df6eef | ||
|
d0c3d907b9 | ||
|
20ee994ebc | ||
|
0d764e6152 | ||
|
0ae69e5b56 | ||
|
ccb02d84d2 | ||
|
5f4217d264 | ||
|
5e9fbfd690 | ||
|
fbe22abe9c | ||
|
283b278524 | ||
|
2f8d7b358a | ||
|
b93df00334 | ||
|
d5faf071b9 | ||
|
a1a2ecbd49 | ||
|
f4ae24b99f | ||
|
652e5ead75 | ||
|
71736bfb43 | ||
|
4bc75db1dc | ||
|
37fbf2b32a | ||
|
05dbf4661c | ||
|
5f5b220fd4 | ||
|
d06875700d | ||
|
822e2f51a3 | ||
|
cee453a8eb | ||
|
a8c7da2b7d | ||
|
02529c9cba | ||
|
bbb1dd3586 | ||
|
8e2fab701c | ||
|
8f44424962 | ||
|
fe503509ea | ||
|
ad2c85d034 | ||
|
b381421f45 | ||
|
a4dce52825 | ||
|
d037f82f60 | ||
|
ba0a3819b9 | ||
|
1e510f311b | ||
|
d033d7ae1b | ||
|
90b34efa1b | ||
|
a2020ff5ce | ||
|
690b1c4667 | ||
|
21bf1cbab8 |
50
.github/workflows/powershell.yml
vendored
Normal file
50
.github/workflows/powershell.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# https://github.com/microsoft/action-psscriptanalyzer
|
||||||
|
# For more information on PSScriptAnalyzer in general, see
|
||||||
|
# https://github.com/PowerShell/PSScriptAnalyzer
|
||||||
|
|
||||||
|
name: PSScriptAnalyzer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
schedule:
|
||||||
|
- cron: '33 20 * * 4'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
permissions:
|
||||||
|
contents: read # for actions/checkout to fetch code
|
||||||
|
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||||
|
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
|
||||||
|
name: PSScriptAnalyzer
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Run PSScriptAnalyzer
|
||||||
|
uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f
|
||||||
|
with:
|
||||||
|
# Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options.
|
||||||
|
# The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules.
|
||||||
|
path: .\source
|
||||||
|
recurse: true
|
||||||
|
# Include your own basic security rules. Removing this option will run all the rules
|
||||||
|
includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText", "PSAvoidUsingPlainTextForPassword", "PSAvoidUsingInvokeExpression", "PSUseApprovedVerbs", "PSAvoidUsingPositionalParameters", "PSAvoidUsingEmptyCatchBlock", "PSAvoidUsingDeprecatedManifestFields", "PSAvoidUsingUserNameAndPasswordParams", "PSAvoidUsingCmdletAliases"'
|
||||||
|
|
||||||
|
output: results.sarif
|
||||||
|
|
||||||
|
# Upload the SARIF file generated in the previous step
|
||||||
|
- name: Upload SARIF results file
|
||||||
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -15,3 +15,4 @@ output/
|
|||||||
markdownissues.txt
|
markdownissues.txt
|
||||||
node_modules
|
node_modules
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
Aligned.xlsx
|
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"Msol"
|
||||||
|
]
|
||||||
|
}
|
138
CHANGELOG.md
138
CHANGELOG.md
@@ -6,6 +6,142 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added output type to functions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Whatif support for `Invoke-M365SecurityAudit`.
|
||||||
|
- Whatif module output and module install process.
|
||||||
|
|
||||||
|
## [0.1.7] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated `Connect-M365Suite` to make `TenantAdminUrl` an optional parameter.
|
||||||
|
- Updated `Invoke-M365SecurityAudit` to make `TenantAdminUrl` an optional parameter.
|
||||||
|
- Improved connection handling and error messaging in `Connect-M365Suite`.
|
||||||
|
- Enhanced `Invoke-M365SecurityAudit` to allow flexible inclusion and exclusion of specific recommendations, IG filters, and profile levels.
|
||||||
|
- SupportsShoudProcess to also bypass connection checks in `Invoke-M365SecurityAudit` as well as Disconnect-M365Suite.
|
||||||
|
|
||||||
|
## [0.1.6] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
|
## [0.1.5] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Updated test definitions for CIS Microsoft 365 Foundations Benchmark for better error handling and object output when errors occur.
|
||||||
|
- Added a parameter to the `Initialize-CISAuditResult` function to allow for a static failed object to be created when an error occurs.
|
||||||
|
- Refactored `Invoke-M365SecurityAudit` to include a new private function `Invoke-TestFunction` for executing test functions and handling errors.
|
||||||
|
- Added a new private function `Measure-AuditResult` to calculate and display audit results.
|
||||||
|
- Enhanced error logging to capture failed test details and display them at the end of the audit.
|
||||||
|
- Added a private function `Get-RequiredModule` to initialize the `$requiredModules` variable for better code organization in the main script.
|
||||||
|
- Updated `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to use `Format-MissingAction` for structuring missing actions into a pipe-separated table format.
|
||||||
|
- Added more verbose logging to `Test-BlockMailForwarding` and improved error handling for better troubleshooting.
|
||||||
|
- Improved `Test-RestrictCustomScripts` to handle long URL lengths better by extracting and replacing common hostnames, and provided detailed output.
|
||||||
|
- Added sorting to output.
|
||||||
|
- Created new functions for improved modularity.
|
||||||
|
- Parameter validation for Excel and CSV path in sync function.
|
||||||
|
- Added Output type to tests.
|
||||||
|
- Added `M365DomainForPWPolicyTest` parameter to `Invoke-M365SecurityAudit` to specify testing only the default domain for password expiration policy when '1.3.1' is included in the tests.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Ensured the `Invoke-TestFunction` returns a `CISAuditResult` object, which is then managed in the `Invoke-M365SecurityAudit` function.
|
||||||
|
- Corrected the usage of the join operation within `$details` in `Test-BlockMailForwarding` to handle arrays properly.
|
||||||
|
- Fixed the logic in `Test-RestrictCustomScripts` to accurately replace and manage URLs, ensuring compliance checks are correctly performed.
|
||||||
|
- Updated the `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to handle the `$allFailures` variable correctly, ensuring accurate pass/fail results.
|
||||||
|
- Fixed the connections in helper CSV and connect function.
|
||||||
|
- Removed verbose preference from `Test-RestrictCustomScripts`.
|
||||||
|
- Ensured that the output in `Test-BlockMailForwarding` does not include extra spaces between table headers and data.
|
||||||
|
- Fixed output in `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` to correctly align with the new table format.
|
||||||
|
- Added step 1 and step 2 in `Test-BlockMailForwarding` details to ensure comprehensive compliance checks.
|
||||||
|
- Fixed the issue with the output in `Test-RestrictCustomScripts` to ensure no extra spaces between table headers and data.
|
||||||
|
|
||||||
|
## [0.1.4] - 2024-05-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Test definitions filter function.
|
||||||
|
- Logging function for future use.
|
||||||
|
- Test grade written to console.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated sync function to include connection info.
|
||||||
|
- Refactored connect/disconnect functions to evaluate needed connections.
|
||||||
|
|
||||||
|
## [0.1.3] - 2024-05-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Array list to store the results of the audit.
|
||||||
|
- Arraylist tests and helper template.
|
||||||
|
- New testing function.
|
||||||
|
- Missing properties to CSV.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Refactored object initialization to source `RecDescription`, `CISControl`, and `CISDescription` properties from the CSV.
|
||||||
|
- Added `Automated` and `Connection` properties to the output object.
|
||||||
|
- All test functions aligned with the test-template.
|
||||||
|
- Initialize-CISAuditResult refactored to use global test definitions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Corrected test-template.
|
||||||
|
- Details added to pass.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- Updated comments and documentation for new functions.
|
||||||
|
|
||||||
|
## [0.1.2] - 2024-04-29
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Automated and organized CSV testing and added test 1.1.1.
|
||||||
|
- Functions to merge tests into an Excel benchmark.
|
||||||
|
- Public function for merging tests.
|
||||||
|
- Testing for guest users under test 1.1.4.
|
||||||
|
- Error handling for `Get-AdminRoleUserLicense`.
|
||||||
|
- Project URI and icon added to manifest.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Format for `TestDefinitions.csv`.
|
||||||
|
- Filename for `Test-AdministrativeAccountCompliance`.
|
||||||
|
- Error handling in test 1.1.1.
|
||||||
|
- Properties for skipping and including tests.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- Updated comments for new functions.
|
||||||
|
- Updated help documentation.
|
||||||
|
- Updated online link in public function.
|
||||||
|
|
||||||
|
## [0.1.1] - 2024-04-02
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed Test-ModernAuthExchangeOnline Profile Level in object.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- CIS Download Notes to Comment-Help Block.
|
||||||
|
- Notes to README.md for CIS Download.
|
||||||
|
|
||||||
|
## [0.1.0-preview0001] - 2024-03-25
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- Initial release of the M365FoundationsCISReport PowerShell module v0.0.1.
|
- Initial release of the M365FoundationsCISReport PowerShell module v0.0.1.
|
||||||
- Function `Invoke-M365SecurityAudit` for conducting a comprehensive security audit in Microsoft 365 environments.
|
- Function `Invoke-M365SecurityAudit` for conducting a comprehensive security audit in Microsoft 365 environments.
|
||||||
- Support for multiple parameter sets including ELevelFilter, IGFilters, RecFilter, and SkipRecFilter to cater to diverse audit requirements.
|
- Support for multiple parameter sets including ELevelFilter, IGFilters, RecFilter, and SkipRecFilter to cater to diverse audit requirements.
|
||||||
@@ -14,4 +150,4 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
- A dynamic test loading system based on CSV input for flexibility in defining audit tests.
|
- A dynamic test loading system based on CSV input for flexibility in defining audit tests.
|
||||||
- Comprehensive verbose logging to detail the steps being performed during an audit.
|
- Comprehensive verbose logging to detail the steps being performed during an audit.
|
||||||
- Comment-help documentation for the `Invoke-M365SecurityAudit` function with examples and usage details.
|
- Comment-help documentation for the `Invoke-M365SecurityAudit` function with examples and usage details.
|
||||||
- Attribution to CIS and licensing information under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License in the README.
|
- Attribution to CIS and licensing information under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License in the README.
|
||||||
|
93
README copy.md
Normal file
93
README copy.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
# M365FoundationsCISReport Module
|
||||||
|
|
||||||
|
## 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:
|
||||||
|
|
||||||
|
- **Non-commercial**: You may not use the material for commercial purposes.
|
||||||
|
- **ShareAlike**: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||||
|
- **Attribution**: Appropriate credit must be given, provide a link to the license, and indicate if changes were made.
|
||||||
|
|
||||||
|
For full license details, please visit [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en).
|
||||||
|
|
||||||
|
[Register for and download CIS Benchmarks](https://www.cisecurity.org/cis-benchmarks)
|
||||||
|
|
||||||
|
## Invoke-M365SecurityAudit
|
||||||
|
### Synopsis
|
||||||
|
Invokes a security audit for Microsoft 365 environments.
|
||||||
|
### Syntax
|
||||||
|
```powershell
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-ELevel <String>] [-ProfileLevel <String>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG1] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG2] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeIG3] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-SkipRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
### Parameters
|
||||||
|
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
|
||||||
|
| - | - | - | - | - | - |
|
||||||
|
| <nobr>TenantAdminUrl</nobr> | | The URL of the tenant admin. This parameter is mandatory. | true | false | |
|
||||||
|
| <nobr>DomainName</nobr> | | The domain name of the Microsoft 365 environment. This parameter is mandatory. | true | false | |
|
||||||
|
| <nobr>ELevel</nobr> | | Specifies the E-Level \(E3 or E5\) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter. | false | false | |
|
||||||
|
| <nobr>ProfileLevel</nobr> | | Specifies the profile level \(L1 or L2\) for the audit. This parameter is optional and can be combined with the ELevel parameter. | false | false | |
|
||||||
|
| <nobr>IncludeIG1</nobr> | | If specified, includes tests where IG1 is true. | false | false | False |
|
||||||
|
| <nobr>IncludeIG2</nobr> | | If specified, includes tests where IG2 is true. | false | false | False |
|
||||||
|
| <nobr>IncludeIG3</nobr> | | If specified, includes tests where IG3 is true. | false | false | False |
|
||||||
|
| <nobr>IncludeRecommendation</nobr> | | Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers. | false | false | |
|
||||||
|
| <nobr>SkipRecommendation</nobr> | | Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers. | false | false | |
|
||||||
|
| <nobr>DoNotConnect</nobr> | | If specified, the cmdlet will not establish a connection to Microsoft 365 services. | false | false | False |
|
||||||
|
| <nobr>DoNotDisconnect</nobr> | | If specified, the cmdlet will not disconnect from Microsoft 365 services after execution. | false | false | False |
|
||||||
|
| <nobr>NoModuleCheck</nobr> | | If specified, the cmdlet will not check for the presence of required modules. | false | false | False |
|
||||||
|
| <nobr>WhatIf</nobr> | wi | | false | false | |
|
||||||
|
| <nobr>Confirm</nobr> | cf | | false | false | |
|
||||||
|
### Inputs
|
||||||
|
- None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
||||||
|
|
||||||
|
### Outputs
|
||||||
|
- CISAuditResult\\[\] The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
||||||
|
|
||||||
|
### Note
|
||||||
|
This module is based on CIS benchmarks and is governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License. For more details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
**EXAMPLE 1**
|
||||||
|
```powershell
|
||||||
|
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.
|
||||||
|
|
||||||
|
**EXAMPLE 2**
|
||||||
|
```powershell
|
||||||
|
Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
|
||||||
|
```
|
||||||
|
Performs an audit including all tests where IG1 is true.
|
||||||
|
|
||||||
|
**EXAMPLE 3**
|
||||||
|
```powershell
|
||||||
|
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.
|
||||||
|
|
||||||
|
**EXAMPLE 4**
|
||||||
|
```powershell
|
||||||
|
$auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
||||||
|
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
||||||
|
```
|
||||||
|
Captures the audit results into a variable and exports them to a CSV file.
|
||||||
|
|
||||||
|
### Links
|
||||||
|
|
||||||
|
- [Online Version: [GitHub Repository URL]](#Online Version: [GitHub Repository URL])
|
18
SECURITY.md
Normal file
18
SECURITY.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 0.1.4 | :white_check_mark: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Use this section to tell people how to report a vulnerability.
|
||||||
|
|
||||||
|
Tell them where to go, how often they can expect to get an update on a
|
||||||
|
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||||
|
declined, etc.
|
BIN
docs/index.html
BIN
docs/index.html
Binary file not shown.
52
helpers/Automation Candidates.md
Normal file
52
helpers/Automation Candidates.md
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Automation Candidates
|
||||||
|
|
||||||
|
## 5.1.1.1 (L1) Ensure Security Defaults is disabled on Azure Active Directory
|
||||||
|
|
||||||
|
- `Connect-MgGraph -Scopes "Policy.Read.All"`
|
||||||
|
- `Get-MgPolicyIdentitySecurityDefaultEnforcementPolicy | ft IsEnabled`
|
||||||
|
|
||||||
|
## 5.1.2.1 (L1) Ensure 'Per-user MFA' is disabled
|
||||||
|
|
||||||
|
- `Connect-MsolService`
|
||||||
|
- Commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$UserList = Get-MsolUser -All | Where-Object { $_.UserType -eq 'Member' }
|
||||||
|
$Report = @()
|
||||||
|
foreach ($user in $UserList) {
|
||||||
|
$PerUserMFAState = $null
|
||||||
|
if ($user.StrongAuthenticationRequirements) {
|
||||||
|
$PerUserMFAState = $user.StrongAuthenticationRequirements.State
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$PerUserMFAState = 'Disabled'
|
||||||
|
}
|
||||||
|
$obj = [pscustomobject][ordered]@{
|
||||||
|
UserPrincipalName = $User.UserPrincipalName
|
||||||
|
DisplayName = $User.DisplayName
|
||||||
|
PerUserMFAState = $PerUserMFAState
|
||||||
|
}
|
||||||
|
$Report += $obj
|
||||||
|
}
|
||||||
|
$Report
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5.1.3.1 (L1) Ensure a dynamic group for guest users is created
|
||||||
|
|
||||||
|
- `Connect-MgGraph -Scopes "Group.Read.All"`
|
||||||
|
- Commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$groups = Get-MgGroup | Where-Object { $_.GroupTypes -contains "DynamicMembership" }
|
||||||
|
$groups | ft DisplayName,GroupTypes,MembershipRule
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6.1.4 (L1) Ensure 'AuditBypassEnabled' is not enabled on mailboxes
|
||||||
|
|
||||||
|
- `Connect-ExchangeOnline`
|
||||||
|
- Commands:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
$MBX = Get-MailboxAuditBypassAssociation -ResultSize unlimited
|
||||||
|
$MBX | where {$_.AuditBypassEnabled -eq $true} | Format-Table Name,AuditBypassEnabled
|
||||||
|
```
|
@@ -1,3 +1,85 @@
|
|||||||
Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
||||||
.\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 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.7"
|
||||||
|
git checkout main
|
||||||
|
git pull origin main
|
||||||
|
git tag -a $ver -m "Release version $ver refactor Update"
|
||||||
|
git push origin $ver
|
||||||
|
"Fix: PR #37"
|
||||||
|
git push origin $ver
|
||||||
|
# git tag -d $ver
|
||||||
|
#>
|
||||||
|
|
||||||
|
# Refresh authentication to ensure the correct scopes
|
||||||
|
gh auth refresh -s project,read:project,write:project,repo
|
||||||
|
|
||||||
|
# Create the project
|
||||||
|
gh project create --owner CriticalSolutionsNetwork --title "Test Validation Project"
|
||||||
|
|
||||||
|
$repoOwner = "CriticalSolutionsNetwork"
|
||||||
|
$repoName = "M365FoundationsCISReport"
|
||||||
|
$directoryPath = ".\source\tests"
|
||||||
|
$projectName = "Test Validation Project"
|
||||||
|
|
||||||
|
# Function to create GitHub issues
|
||||||
|
function Create-GitHubIssue {
|
||||||
|
param (
|
||||||
|
[string]$title,
|
||||||
|
[string]$body,
|
||||||
|
[string]$project
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the issue and add it to the specified project
|
||||||
|
$issue = gh issue create --repo "$repoOwner/$repoName" --title "$title" --body "$body" --project "$project"
|
||||||
|
return $issue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load test definitions from CSV
|
||||||
|
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
|
||||||
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
|
|
||||||
|
# Iterate over each .ps1 file in the directory
|
||||||
|
Get-ChildItem -Path $directoryPath -Filter "*.ps1" | ForEach-Object {
|
||||||
|
$fileName = $_.Name
|
||||||
|
$testDefinition = $testDefinitions | Where-Object { $_.TestFileName -eq $fileName }
|
||||||
|
|
||||||
|
if ($testDefinition) {
|
||||||
|
$rec = $testDefinition.Rec
|
||||||
|
$elevel = $testDefinition.ELevel
|
||||||
|
$profileLevel = $testDefinition.ProfileLevel
|
||||||
|
$ig1 = $testDefinition.IG1
|
||||||
|
$ig2 = $testDefinition.IG2
|
||||||
|
$ig3 = $testDefinition.IG3
|
||||||
|
$connection = $testDefinition.Connection
|
||||||
|
|
||||||
|
$issueTitle = "Rec: $rec - Validate $fileName, ELevel: $elevel, ProfileLevel: $profileLevel, IG1: $ig1, IG2: $ig2, IG3: $ig3, Connection: $connection"
|
||||||
|
$issueBody = @"
|
||||||
|
# Validation for $fileName
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [ ] Validate test for a pass
|
||||||
|
- Description of passing criteria:
|
||||||
|
- [ ] Validate test for a fail
|
||||||
|
- Description of failing criteria:
|
||||||
|
- [ ] Add notes and observations
|
||||||
|
- Placeholder for additional notes:
|
||||||
|
"@
|
||||||
|
|
||||||
|
# Create the issue using GitHub CLI
|
||||||
|
try {
|
||||||
|
Create-GitHubIssue -title "$issueTitle" -body "$issueBody" -project "$projectName"
|
||||||
|
Write-Output "Created issue for $fileName"
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to create issue for $fileName : $_"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Introduce a delay of 2 seconds
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
} else {
|
||||||
|
Write-Warning "No matching test definition found for $fileName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -60,7 +60,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
if ($missingActions) {
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
$formattedActions = Format-MissingAction $missingActions
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
||||||
}
|
}
|
||||||
# Mark the user as processed
|
# Mark the user as processed
|
||||||
@@ -83,7 +83,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
function Format-MissingAction {
|
||||||
param ([array]$missingActions)
|
param ([array]$missingActions)
|
||||||
|
|
||||||
$actionGroups = @{
|
$actionGroups = @{
|
||||||
|
@@ -60,7 +60,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
if ($missingActions) {
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
$formattedActions = Format-MissingAction $missingActions
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -92,7 +92,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
function Format-MissingAction {
|
||||||
param ([array]$missingActions)
|
param ([array]$missingActions)
|
||||||
|
|
||||||
$actionGroups = @{
|
$actionGroups = @{
|
||||||
|
97
helpers/CIS 365 v3.0.0 Controls/Test-Template.ps1
Normal file
97
helpers/CIS 365 v3.0.0 Controls/Test-Template.ps1
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
function Test-Template {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
# Parameters can be added if needed
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
# Initialization code, if needed
|
||||||
|
# Load necessary scripts, define variables, etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
# Fetch relevant data
|
||||||
|
# Example: $data = Get-SomeData
|
||||||
|
|
||||||
|
# Process the data to evaluate compliance
|
||||||
|
# Example: $compliantItems = $data | Where-Object { $_.Property -eq 'ExpectedValue' }
|
||||||
|
# Example: $nonCompliantItems = $data | Where-Object { $_.Property -ne 'ExpectedValue' }
|
||||||
|
|
||||||
|
# Prepare failure reasons for non-compliant items
|
||||||
|
$failureReasons = $nonCompliantItems | ForEach-Object {
|
||||||
|
# Example: "Item: $($_.Name) - Reason: Missing expected value"
|
||||||
|
}
|
||||||
|
$failureReasons = $failureReasons -join "`n"
|
||||||
|
|
||||||
|
# Prepare details for non-compliant items
|
||||||
|
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
|
||||||
|
# Example: "$($_.Name) - Value: $($_.Property)"
|
||||||
|
}
|
||||||
|
$nonCompliantDetails = $nonCompliantDetails -join "`n"
|
||||||
|
|
||||||
|
# Prepare details based on compliance
|
||||||
|
$details = if ($nonCompliantItems) {
|
||||||
|
"Non-Compliant Items: $($nonCompliantItems.Count)`nDetails:`n$nonCompliantDetails"
|
||||||
|
} else {
|
||||||
|
"Compliant Items: $($compliantItems.Count)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$auditResult.Status = if ($nonCompliantItems) { 'Fail' } else { 'Pass' }
|
||||||
|
$auditResult.ELevel = 'E3' # Modify as needed
|
||||||
|
$auditResult.ProfileLevel = 'L1' # Modify as needed
|
||||||
|
$auditResult.Rec = '1.1.1' # Modify as needed
|
||||||
|
$auditResult.RecDescription = "Description of the recommendation" # Modify as needed
|
||||||
|
$auditResult.CISControlVer = 'v8' # Modify as needed
|
||||||
|
$auditResult.CISControl = "5.4" # Modify as needed
|
||||||
|
$auditResult.CISDescription = "Description of the CIS control" # Modify as needed
|
||||||
|
$auditResult.IG1 = $true # Modify as needed
|
||||||
|
$auditResult.IG2 = $true # Modify as needed
|
||||||
|
$auditResult.IG3 = $true # Modify as needed
|
||||||
|
$auditResult.Result = $nonCompliantItems.Count -eq 0
|
||||||
|
$auditResult.Details = $details
|
||||||
|
$auditResult.FailureReason = if ($nonCompliantItems) {
|
||||||
|
"Non-compliant items:`n$failureReasons"
|
||||||
|
} else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Example output object for a pass result
|
||||||
|
# Status : Pass
|
||||||
|
# ELevel : E3
|
||||||
|
# ProfileLevel : L2
|
||||||
|
# Rec : 8.1.1
|
||||||
|
# RecDescription : Ensure external file sharing in Teams is enabled for only approved cloud storage services
|
||||||
|
# CISControlVer : v8
|
||||||
|
# CISControl : 3.3
|
||||||
|
# CISDescription : Configure Data Access Control Lists
|
||||||
|
# IG1 : True
|
||||||
|
# IG2 : True
|
||||||
|
# IG3 : True
|
||||||
|
# Result : True
|
||||||
|
# Details : Compliant Items: 5
|
||||||
|
# FailureReason : N/A
|
||||||
|
|
||||||
|
# Example output object for a fail result
|
||||||
|
# Status : Fail
|
||||||
|
# ELevel : E3
|
||||||
|
# ProfileLevel : L2
|
||||||
|
# Rec : 8.1.1
|
||||||
|
# RecDescription : Ensure external file sharing in Teams is enabled for only approved cloud storage services
|
||||||
|
# CISControlVer : v8
|
||||||
|
# CISControl : 3.3
|
||||||
|
# CISDescription : Configure Data Access Control Lists
|
||||||
|
# IG1 : True
|
||||||
|
# IG2 : True
|
||||||
|
# IG3 : True
|
||||||
|
# Result : False
|
||||||
|
# Details : Non-Compliant Items: 2
|
||||||
|
# FailureReason : Non-compliant items:`nUsername | Roles | HybridStatus | Missing Licence
|
||||||
|
}
|
||||||
|
|
||||||
|
end {
|
||||||
|
# Return the audit result
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
45
helpers/Get-AdminRoleUserLicense.ps1
Normal file
45
helpers/Get-AdminRoleUserLicense.ps1
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
function Get-AdminRoleUserLicense {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[bool]$SkipGraphConnection = $false
|
||||||
|
)
|
||||||
|
|
||||||
|
# Connect to Microsoft Graph if not skipping connection
|
||||||
|
if (-not $SkipGraphConnection) {
|
||||||
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
|
||||||
|
}
|
||||||
|
|
||||||
|
$adminRoleUsers = @()
|
||||||
|
$userIds = @()
|
||||||
|
$adminroles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
|
||||||
|
|
||||||
|
foreach ($role in $adminroles) {
|
||||||
|
$usersInRole = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
|
||||||
|
|
||||||
|
foreach ($user in $usersInRole) {
|
||||||
|
$userIds += $user.PrincipalId
|
||||||
|
$userDetails = Get-MgUser -UserId $user.PrincipalId -Property "DisplayName, UserPrincipalName, Id, onPremisesSyncEnabled"
|
||||||
|
|
||||||
|
$adminRoleUsers += [PSCustomObject]@{
|
||||||
|
RoleName = $role.DisplayName
|
||||||
|
UserName = $userDetails.DisplayName
|
||||||
|
UserPrincipalName = $userDetails.UserPrincipalName
|
||||||
|
UserId = $userDetails.Id
|
||||||
|
HybridUser = $userDetails.onPremisesSyncEnabled
|
||||||
|
Licenses = "" # Placeholder for licenses, to be filled later
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($userId in $userIds | Select-Object -Unique) {
|
||||||
|
$licenses = Get-MgUserLicenseDetail -UserId $userId
|
||||||
|
$licenseList = ($licenses.SkuPartNumber -join '|')
|
||||||
|
|
||||||
|
$adminRoleUsers | Where-Object { $_.UserId -eq $userId } | ForEach-Object {
|
||||||
|
$_.Licenses = $licenseList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $adminRoleUsers
|
||||||
|
}
|
@@ -2,6 +2,8 @@ class CISAuditResult {
|
|||||||
[string]$Status
|
[string]$Status
|
||||||
[string]$ELevel
|
[string]$ELevel
|
||||||
[string]$ProfileLevel
|
[string]$ProfileLevel
|
||||||
|
[bool]$Automated
|
||||||
|
[string]$Connection
|
||||||
[string]$Rec
|
[string]$Rec
|
||||||
[string]$RecDescription
|
[string]$RecDescription
|
||||||
[string]$CISControlVer = 'v8'
|
[string]$CISControlVer = 'v8'
|
||||||
|
@@ -102,10 +102,10 @@ PrivateData = @{
|
|||||||
LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en'
|
LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en'
|
||||||
|
|
||||||
# A URL to the main website for this project.
|
# A URL to the main website for this project.
|
||||||
# ProjectUri = ''
|
ProjectUri = 'https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport'
|
||||||
|
|
||||||
# A URL to an icon representing this module.
|
# A URL to an icon representing this module.
|
||||||
# IconUri = ''
|
IconUri = 'https://csn-source.s3.us-east-2.amazonaws.com/CSN-Icon.png'
|
||||||
|
|
||||||
# ReleaseNotes of this module
|
# ReleaseNotes of this module
|
||||||
ReleaseNotes = ''
|
ReleaseNotes = ''
|
||||||
|
@@ -1,33 +1,37 @@
|
|||||||
function Assert-ModuleAvailability {
|
function Assert-ModuleAvailability {
|
||||||
|
[OutputType([void]) ]
|
||||||
param(
|
param(
|
||||||
[string]$ModuleName,
|
[string]$ModuleName,
|
||||||
[string]$RequiredVersion,
|
[string]$RequiredVersion,
|
||||||
[string]$SubModuleName
|
[string[]]$SubModules = @()
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
||||||
|
|
||||||
if ($null -eq $module) {$auditResult.Profile
|
if ($null -eq $module) {
|
||||||
Write-Host "Installing $ModuleName module..."
|
Write-Information "Installing $ModuleName module..." -InformationAction Continue
|
||||||
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
||||||
}
|
}
|
||||||
elseif ($module.Version -lt [version]$RequiredVersion) {
|
elseif ($module.Version -lt [version]$RequiredVersion) {
|
||||||
Write-Host "Updating $ModuleName module to required version..."
|
Write-Information "Updating $ModuleName module to required version..." -InformationAction Continue
|
||||||
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Host "$ModuleName module is already at required version or newer."
|
Write-Information "$ModuleName module is already at required version or newer." -InformationAction Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($SubModuleName) {
|
if ($SubModules.Count -gt 0) {
|
||||||
Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
foreach ($subModule in $SubModules) {
|
||||||
}
|
Write-Information "Importing submodule $ModuleName.$subModule..." -InformationAction Continue
|
||||||
else {
|
Import-Module -Name "$ModuleName.$subModule" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Information "Importing module $ModuleName..." -InformationAction Continue
|
||||||
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "An error occurred with module $ModuleName`: $_"
|
Write-Warning "An error occurred with module $ModuleName`: $_"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,56 +1,59 @@
|
|||||||
function Connect-M365Suite {
|
function Connect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
# Parameter to specify the SharePoint Online Tenant Admin URL
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string]$TenantAdminUrl
|
[string[]]$RequiredConnections
|
||||||
)
|
)
|
||||||
$VerbosePreference = "SilentlyContinue"
|
|
||||||
|
$VerbosePreference = "SilentlyContinue"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
|
||||||
|
Connect-AzureAD | Out-Null
|
||||||
|
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
# Attempt to connect to Azure Active Directory
|
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
|
|
||||||
Connect-AzureAD | Out-Null
|
|
||||||
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
|
||||||
|
|
||||||
# Attempt to connect to Exchange Online
|
|
||||||
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
|
||||||
Connect-ExchangeOnline | Out-Null
|
|
||||||
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
|
||||||
try {
|
|
||||||
# Attempt to connect to Microsoft Graph with specified scopes
|
|
||||||
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
||||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
try {
|
||||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
||||||
}
|
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||||
catch {
|
}
|
||||||
Write-Host "Failed to connect o MgGraph, attempting device auth." -ForegroundColor Yellow
|
catch {
|
||||||
# Attempt to connect to Microsoft Graph with specified scopes
|
Write-Host "Failed to connect to MgGraph, attempting device auth." -ForegroundColor Yellow
|
||||||
Write-Host "Connecting to Microsoft Graph using device auth with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
|
||||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
|
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Validate SharePoint Online Tenant Admin URL
|
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
if (-not $TenantAdminUrl) {
|
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
||||||
throw "SharePoint Online Tenant Admin URL is required."
|
Connect-ExchangeOnline | Out-Null
|
||||||
|
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
||||||
}
|
}
|
||||||
|
|
||||||
# Attempt to connect to SharePoint Online
|
if ($RequiredConnections -contains "SPO") {
|
||||||
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
|
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
|
||||||
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
||||||
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
|
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
# Attempt to connect to Microsoft Teams
|
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||||
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
|
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
|
||||||
Connect-MicrosoftTeams | Out-Null
|
Connect-MicrosoftTeams | Out-Null
|
||||||
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
|
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
$VerbosePreference = "Continue"
|
$VerbosePreference = "Continue"
|
||||||
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
|
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
|
||||||
throw $_
|
throw $_
|
||||||
}
|
}
|
||||||
|
|
||||||
$VerbosePreference = "Continue"
|
$VerbosePreference = "Continue"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,39 +1,60 @@
|
|||||||
function Disconnect-M365Suite {
|
function Disconnect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string[]]$RequiredConnections
|
||||||
|
)
|
||||||
|
|
||||||
# Clean up sessions
|
# Clean up sessions
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
|
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||||
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
|
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
|
||||||
|
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Exchange Online: $_"
|
Write-Warning "Failed to disconnect from Exchange Online: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
|
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
|
||||||
Disconnect-AzureAD | Out-Null
|
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
|
||||||
|
Disconnect-AzureAD | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Azure AD: $_"
|
Write-Warning "Failed to disconnect from Azure AD: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
if ($RequiredConnections -contains "Microsoft Graph") {
|
||||||
Disconnect-MgGraph | Out-Null
|
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
||||||
|
Disconnect-MgGraph | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
|
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
|
if ($RequiredConnections -contains "SPO") {
|
||||||
Disconnect-SPOService | Out-Null
|
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
|
||||||
|
Disconnect-SPOService | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from SharePoint Online: $_"
|
Write-Warning "Failed to disconnect from SharePoint Online: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
|
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||||
Disconnect-MicrosoftTeams | Out-Null
|
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
|
||||||
|
Disconnect-MicrosoftTeams | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
|
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
|
||||||
}
|
}
|
||||||
Write-Host "All sessions have been disconnected." -ForegroundColor Green
|
|
||||||
|
Write-Host "All necessary sessions have been disconnected." -ForegroundColor Green
|
||||||
}
|
}
|
29
source/Private/Format-MissingAction.ps1
Normal file
29
source/Private/Format-MissingAction.ps1
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
function Format-MissingAction {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([hashtable])]
|
||||||
|
param (
|
||||||
|
[array]$missingActions
|
||||||
|
)
|
||||||
|
|
||||||
|
$actionGroups = @{
|
||||||
|
"Admin" = @()
|
||||||
|
"Delegate" = @()
|
||||||
|
"Owner" = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($action in $missingActions) {
|
||||||
|
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
|
||||||
|
$type = $matches[1]
|
||||||
|
$actionName = $matches[2]
|
||||||
|
$actionGroups[$type] += $actionName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$formattedResults = @{
|
||||||
|
Admin = $actionGroups["Admin"] -join ', '
|
||||||
|
Delegate = $actionGroups["Delegate"] -join ', '
|
||||||
|
Owner = $actionGroups["Owner"] -join ', '
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedResults
|
||||||
|
}
|
19
source/Private/Format-RequiredModuleList.ps1
Normal file
19
source/Private/Format-RequiredModuleList.ps1
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
function Format-RequiredModuleList {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[System.Object[]]$RequiredModules
|
||||||
|
)
|
||||||
|
|
||||||
|
$requiredModulesFormatted = ""
|
||||||
|
foreach ($module in $RequiredModules) {
|
||||||
|
if ($module.SubModules -and $module.SubModules.Count -gt 0) {
|
||||||
|
$subModulesFormatted = $module.SubModules -join ', '
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName) (SubModules: $subModulesFormatted), "
|
||||||
|
} else {
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName), "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $requiredModulesFormatted.TrimEnd(", ")
|
||||||
|
}
|
24
source/Private/Get-MostCommonWord.ps1
Normal file
24
source/Private/Get-MostCommonWord.ps1
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
function Get-MostCommonWord {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$InputStrings
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine all strings into one large string
|
||||||
|
$allText = $InputStrings -join ' '
|
||||||
|
|
||||||
|
# Split the large string into words
|
||||||
|
$words = $allText -split '\s+'
|
||||||
|
|
||||||
|
# Group words and count occurrences
|
||||||
|
$wordGroups = $words | Group-Object | Sort-Object Count -Descending
|
||||||
|
|
||||||
|
# Return the most common word if it occurs at least 3 times
|
||||||
|
if ($wordGroups.Count -gt 0 -and $wordGroups[0].Count -ge 3) {
|
||||||
|
return $wordGroups[0].Name
|
||||||
|
} else {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
}
|
31
source/Private/Get-RequiredModule.ps1
Normal file
31
source/Private/Get-RequiredModule.ps1
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
function Get-RequiredModule {
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'AuditFunction')]
|
||||||
|
[OutputType([System.Object[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'AuditFunction')]
|
||||||
|
[switch]$AuditFunction,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SyncFunction')]
|
||||||
|
[switch]$SyncFunction
|
||||||
|
)
|
||||||
|
|
||||||
|
switch ($PSCmdlet.ParameterSetName) {
|
||||||
|
'AuditFunction' {
|
||||||
|
return @(
|
||||||
|
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
|
||||||
|
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() },
|
||||||
|
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||||
|
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
|
||||||
|
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'SyncFunction' {
|
||||||
|
return @(
|
||||||
|
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9"; SubModules = @() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
throw "Please specify either -AuditFunction or -SyncFunction switch."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
source/Private/Get-TestDefinitionsObject.ps1
Normal file
65
source/Private/Get-TestDefinitionsObject.ps1
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
function Get-TestDefinitionsObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([object[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[object[]]$TestDefinitions,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ParameterSetName,
|
||||||
|
|
||||||
|
[string]$ELevel,
|
||||||
|
[string]$ProfileLevel,
|
||||||
|
[string[]]$IncludeRecommendation,
|
||||||
|
[string[]]$SkipRecommendation
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Verbose "Initial test definitions count: $($TestDefinitions.Count)"
|
||||||
|
|
||||||
|
switch ($ParameterSetName) {
|
||||||
|
'ELevelFilter' {
|
||||||
|
Write-Verbose "Applying ELevelFilter"
|
||||||
|
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
|
||||||
|
Write-Verbose "Filtering on ELevel = $ELevel and ProfileLevel = $ProfileLevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($null -ne $ELevel) {
|
||||||
|
Write-Verbose "Filtering on ELevel = $ELevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ELevel -eq $ELevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($null -ne $ProfileLevel) {
|
||||||
|
Write-Verbose "Filtering on ProfileLevel = $ProfileLevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ProfileLevel -eq $ProfileLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'IG1Filter' {
|
||||||
|
Write-Verbose "Applying IG1Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'IG2Filter' {
|
||||||
|
Write-Verbose "Applying IG2Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'IG3Filter' {
|
||||||
|
Write-Verbose "Applying IG3Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'RecFilter' {
|
||||||
|
Write-Verbose "Applying RecFilter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
|
||||||
|
}
|
||||||
|
'SkipRecFilter' {
|
||||||
|
Write-Verbose "Applying SkipRecFilter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
||||||
|
return $TestDefinitions
|
||||||
|
}
|
28
source/Private/Get-UniqueConnection.ps1
Normal file
28
source/Private/Get-UniqueConnection.ps1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
function Get-UniqueConnection {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Connections
|
||||||
|
)
|
||||||
|
|
||||||
|
$uniqueConnections = @()
|
||||||
|
|
||||||
|
if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "AzureAD"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "Microsoft Graph"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "EXO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "SPO") {
|
||||||
|
$uniqueConnections += "SPO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Teams" -or $Connections -contains "Microsoft Teams | EXO") {
|
||||||
|
$uniqueConnections += "Microsoft Teams"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uniqueConnections | Sort-Object -Unique
|
||||||
|
}
|
62
source/Private/Initialize-CISAuditResult.ps1
Normal file
62
source/Private/Initialize-CISAuditResult.ps1
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
function Initialize-CISAuditResult {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Rec,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
|
[bool]$Result,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
|
[string]$Status,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
|
[string]$Details,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
|
[string]$FailureReason,
|
||||||
|
|
||||||
|
[Parameter(ParameterSetName = 'Error')]
|
||||||
|
[switch]$Failure
|
||||||
|
)
|
||||||
|
|
||||||
|
# Import the test definitions CSV file
|
||||||
|
$testDefinitions = $script:TestDefinitionsObject
|
||||||
|
|
||||||
|
# Find the row that matches the provided recommendation (Rec)
|
||||||
|
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $Rec }
|
||||||
|
|
||||||
|
if (-not $testDefinition) {
|
||||||
|
throw "Test definition for recommendation '$Rec' not found."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create an instance of CISAuditResult and populate it
|
||||||
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$auditResult.Rec = $Rec
|
||||||
|
$auditResult.ELevel = $testDefinition.ELevel
|
||||||
|
$auditResult.ProfileLevel = $testDefinition.ProfileLevel
|
||||||
|
$auditResult.IG1 = [bool]::Parse($testDefinition.IG1)
|
||||||
|
$auditResult.IG2 = [bool]::Parse($testDefinition.IG2)
|
||||||
|
$auditResult.IG3 = [bool]::Parse($testDefinition.IG3)
|
||||||
|
$auditResult.RecDescription = $testDefinition.RecDescription
|
||||||
|
$auditResult.CISControl = $testDefinition.CISControl
|
||||||
|
$auditResult.CISDescription = $testDefinition.CISDescription
|
||||||
|
$auditResult.Automated = [bool]::Parse($testDefinition.Automated)
|
||||||
|
$auditResult.Connection = $testDefinition.Connection
|
||||||
|
$auditResult.CISControlVer = 'v8'
|
||||||
|
|
||||||
|
if ($PSCmdlet.ParameterSetName -eq 'Full') {
|
||||||
|
$auditResult.Result = $Result
|
||||||
|
$auditResult.Status = $Status
|
||||||
|
$auditResult.Details = $Details
|
||||||
|
$auditResult.FailureReason = $FailureReason
|
||||||
|
} elseif ($PSCmdlet.ParameterSetName -eq 'Error') {
|
||||||
|
$auditResult.Result = $false
|
||||||
|
$auditResult.Status = 'Fail'
|
||||||
|
$auditResult.Details = "An error occurred while processing the test."
|
||||||
|
$auditResult.FailureReason = "Initialization error: Failed to process the test."
|
||||||
|
}
|
||||||
|
|
||||||
|
return $auditResult
|
||||||
|
}
|
35
source/Private/Invoke-TestFunction.ps1
Normal file
35
source/Private/Invoke-TestFunction.ps1
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
function Invoke-TestFunction {
|
||||||
|
[OutputType([CISAuditResult[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[PSObject]$FunctionFile,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$DomainName
|
||||||
|
)
|
||||||
|
|
||||||
|
$functionName = $FunctionFile.BaseName
|
||||||
|
$functionCmd = Get-Command -Name $functionName
|
||||||
|
|
||||||
|
# Check if the test function needs DomainName parameter
|
||||||
|
$paramList = @{}
|
||||||
|
if ('DomainName' -in $functionCmd.Parameters.Keys) {
|
||||||
|
$paramList.DomainName = $DomainName
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use splatting to pass parameters
|
||||||
|
Write-Verbose "Running $functionName..."
|
||||||
|
try {
|
||||||
|
$result = & $functionName @paramList
|
||||||
|
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Test = $functionName; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $functionName -Failure
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
32
source/Private/Measure-AuditResult.ps1
Normal file
32
source/Private/Measure-AuditResult.ps1
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
function Measure-AuditResult {
|
||||||
|
[OutputType([void])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[System.Collections.ArrayList]$AllAuditResults,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[System.Collections.ArrayList]$FailedTests
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate the total number of tests
|
||||||
|
$totalTests = $AllAuditResults.Count
|
||||||
|
|
||||||
|
# Calculate the number of passed tests
|
||||||
|
$passedTests = $AllAuditResults.ToArray() | Where-Object { $_.Result -eq $true } | Measure-Object | Select-Object -ExpandProperty Count
|
||||||
|
|
||||||
|
# Calculate the pass percentage
|
||||||
|
$passPercentage = if ($totalTests -eq 0) { 0 } else { [math]::Round(($passedTests / $totalTests) * 100, 2) }
|
||||||
|
|
||||||
|
# Display the pass percentage to the user
|
||||||
|
Write-Host "Audit completed. $passedTests out of $totalTests tests passed." -ForegroundColor Cyan
|
||||||
|
Write-Host "Your passing percentage is $passPercentage%."
|
||||||
|
|
||||||
|
# Display details of failed tests
|
||||||
|
if ($FailedTests.Count -gt 0) {
|
||||||
|
Write-Host "The following tests failed to complete:" -ForegroundColor Red
|
||||||
|
foreach ($failedTest in $FailedTests) {
|
||||||
|
Write-Host "Test: $($failedTest.Test)" -ForegroundColor Yellow
|
||||||
|
Write-Host "Error: $($failedTest.Error)" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
source/Private/Merge-CISExcelAndCsvData.ps1
Normal file
42
source/Private/Merge-CISExcelAndCsvData.ps1
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
function Merge-CISExcelAndCsvData {
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
|
[OutputType([PSCustomObject[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ExcelPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$WorksheetName,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
|
||||||
|
[string]$CsvPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput')]
|
||||||
|
[CISAuditResult[]]$AuditResults
|
||||||
|
)
|
||||||
|
|
||||||
|
process {
|
||||||
|
# Import data from Excel
|
||||||
|
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
|
||||||
|
|
||||||
|
# Import data from CSV or use provided object
|
||||||
|
$csvData = if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
|
||||||
|
Import-Csv -Path $CsvPath
|
||||||
|
} else {
|
||||||
|
$AuditResults
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over each item in the imported Excel object and merge with CSV data or audit results
|
||||||
|
$mergedData = foreach ($item in $import) {
|
||||||
|
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
|
||||||
|
if ($csvRow) {
|
||||||
|
New-MergedObject -ExcelItem $item -CsvRow $csvRow
|
||||||
|
} else {
|
||||||
|
New-MergedObject -ExcelItem $item -CsvRow ([PSCustomObject]@{Connection=$null;Status=$null; Details=$null; FailureReason=$null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return the merged data
|
||||||
|
return $mergedData
|
||||||
|
}
|
||||||
|
}
|
22
source/Private/New-MergedObject.ps1
Normal file
22
source/Private/New-MergedObject.ps1
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
function New-MergedObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([PSCustomObject])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[psobject]$ExcelItem,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[psobject]$CsvRow
|
||||||
|
)
|
||||||
|
|
||||||
|
$newObject = New-Object PSObject
|
||||||
|
|
||||||
|
foreach ($property in $ExcelItem.PSObject.Properties) {
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
|
||||||
|
}
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Connection' -Value $CsvRow.Connection
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $CsvRow.Status
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $CsvRow.Details
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $CsvRow.FailureReason
|
||||||
|
return $newObject
|
||||||
|
}
|
22
source/Private/Test-IsAdmin.ps1
Normal file
22
source/Private/Test-IsAdmin.ps1
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
function Test-IsAdmin {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Checks if the current user is an administrator on the machine.
|
||||||
|
.DESCRIPTION
|
||||||
|
This private function returns a Boolean value indicating whether
|
||||||
|
the current user has administrator privileges on the machine.
|
||||||
|
It does this by creating a new WindowsPrincipal object, passing
|
||||||
|
in a WindowsIdentity object representing the current user, and
|
||||||
|
then checking if that principal is in the Administrator role.
|
||||||
|
.INPUTS
|
||||||
|
None.
|
||||||
|
.OUTPUTS
|
||||||
|
Boolean. Returns True if the current user is an administrator, and False otherwise.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Test-IsAdmin
|
||||||
|
True
|
||||||
|
#>
|
||||||
|
|
||||||
|
# Create a new WindowsPrincipal object for the current user and check if it is in the Administrator role
|
||||||
|
(New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
|
||||||
|
}
|
34
source/Private/Update-CISExcelWorksheet.ps1
Normal file
34
source/Private/Update-CISExcelWorksheet.ps1
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
function Update-CISExcelWorksheet {
|
||||||
|
[OutputType([void])]
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ExcelPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$WorksheetName,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[psobject[]]$Data,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[int]$StartingRowIndex = 2 # Default starting row index, assuming row 1 has headers
|
||||||
|
)
|
||||||
|
|
||||||
|
process {
|
||||||
|
# Load the existing Excel sheet
|
||||||
|
$excelPackage = Open-ExcelPackage -Path $ExcelPath
|
||||||
|
$worksheet = $excelPackage.Workbook.Worksheets[$WorksheetName]
|
||||||
|
|
||||||
|
if (-not $worksheet) {
|
||||||
|
throw "Worksheet '$WorksheetName' not found in '$ExcelPath'"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Update the worksheet with the provided data
|
||||||
|
Update-WorksheetCell -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
|
||||||
|
|
||||||
|
# Save and close the Excel package
|
||||||
|
Close-ExcelPackage $excelPackage
|
||||||
|
}
|
||||||
|
}
|
29
source/Private/Update-WorksheetCell.ps1
Normal file
29
source/Private/Update-WorksheetCell.ps1
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
function Update-WorksheetCell {
|
||||||
|
[OutputType([void])]
|
||||||
|
param (
|
||||||
|
$Worksheet,
|
||||||
|
$Data,
|
||||||
|
$StartingRowIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check and set headers
|
||||||
|
$firstItem = $Data[0]
|
||||||
|
$colIndex = 1
|
||||||
|
foreach ($property in $firstItem.PSObject.Properties) {
|
||||||
|
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
||||||
|
$Worksheet.Cells[1, $colIndex].Value = $property.Name
|
||||||
|
}
|
||||||
|
$colIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over each row in the data and update cells
|
||||||
|
$rowIndex = $StartingRowIndex
|
||||||
|
foreach ($item in $Data) {
|
||||||
|
$colIndex = 1
|
||||||
|
foreach ($property in $item.PSObject.Properties) {
|
||||||
|
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
|
||||||
|
$colIndex++
|
||||||
|
}
|
||||||
|
$rowIndex++
|
||||||
|
}
|
||||||
|
}
|
212
source/Private/Write-AuditLog.ps1
Normal file
212
source/Private/Write-AuditLog.ps1
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
function Write-AuditLog {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Writes log messages to the console and updates the script-wide log variable.
|
||||||
|
.DESCRIPTION
|
||||||
|
The Write-AuditLog function writes log messages to the console based on the severity (Verbose, Warning, or Error) and updates
|
||||||
|
the script-wide log variable ($script:LogString) with the log entry. You can use the Start, End, and EndFunction switches to
|
||||||
|
manage the lifecycle of the logging.
|
||||||
|
.INPUTS
|
||||||
|
System.String
|
||||||
|
You can pipe a string to the Write-AuditLog function as the Message parameter.
|
||||||
|
You can also pipe an object with a Severity property as the Severity parameter.
|
||||||
|
.OUTPUTS
|
||||||
|
None
|
||||||
|
The Write-AuditLog function doesn't output any objects to the pipeline. It writes messages to the console and updates the
|
||||||
|
script-wide log variable ($script:LogString).
|
||||||
|
.PARAMETER BeginFunction
|
||||||
|
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
|
||||||
|
.PARAMETER Message
|
||||||
|
The message string to log.
|
||||||
|
.PARAMETER Severity
|
||||||
|
The severity of the log message. Accepted values are 'Information', 'Warning', and 'Error'. Defaults to 'Information'.
|
||||||
|
.PARAMETER Start
|
||||||
|
Initializes the script-wide log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
|
||||||
|
.PARAMETER End
|
||||||
|
Sets the message to "End Log" and exports the log to a CSV file if the OutputPath parameter is provided.
|
||||||
|
.PARAMETER EndFunction
|
||||||
|
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
|
||||||
|
.PARAMETER OutputPath
|
||||||
|
The file path for exporting the log to a CSV file when using the End switch.
|
||||||
|
.EXAMPLE
|
||||||
|
Write-AuditLog -Message "This is a test message."
|
||||||
|
|
||||||
|
Writes a test message with the default severity (Information) to the console and adds it to the log variable.
|
||||||
|
.EXAMPLE
|
||||||
|
Write-AuditLog -Message "This is a warning message." -Severity "Warning"
|
||||||
|
|
||||||
|
Writes a warning message to the console and adds it to the log variable.
|
||||||
|
.EXAMPLE
|
||||||
|
Write-AuditLog -Start
|
||||||
|
|
||||||
|
Initializes the log variable and sets the message to "Begin [FunctionName] Log.", where FunctionName is the name of the calling function.
|
||||||
|
.EXAMPLE
|
||||||
|
Write-AuditLog -BeginFunction
|
||||||
|
|
||||||
|
Sets the message to "Begin [FunctionName] function log.", where FunctionName is the name of the calling function, and adds it to the log variable.
|
||||||
|
.EXAMPLE
|
||||||
|
Write-AuditLog -EndFunction
|
||||||
|
|
||||||
|
Sets the message to "End [FunctionName] log.", where FunctionName is the name of the calling function, and adds it to the log variable.
|
||||||
|
.EXAMPLE
|
||||||
|
Write-AuditLog -End -OutputPath "C:\Logs\auditlog.csv"
|
||||||
|
|
||||||
|
Sets the message to "End Log", adds it to the log variable, and exports the log to a CSV file.
|
||||||
|
.NOTES
|
||||||
|
Author: DrIOSx
|
||||||
|
#>
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'Default')]
|
||||||
|
param(
|
||||||
|
###
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
HelpMessage = 'Input a Message string.',
|
||||||
|
Position = 0,
|
||||||
|
ParameterSetName = 'Default',
|
||||||
|
ValueFromPipeline = $true
|
||||||
|
)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string]$Message,
|
||||||
|
###
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
HelpMessage = 'Information, Warning or Error.',
|
||||||
|
Position = 1,
|
||||||
|
ParameterSetName = 'Default',
|
||||||
|
ValueFromPipelineByPropertyName = $true
|
||||||
|
)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[ValidateSet('Information', 'Warning', 'Error')]
|
||||||
|
[string]$Severity = 'Information',
|
||||||
|
###
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
ParameterSetName = 'End'
|
||||||
|
)]
|
||||||
|
[switch]$End,
|
||||||
|
###
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
ParameterSetName = 'BeginFunction'
|
||||||
|
)]
|
||||||
|
[switch]$BeginFunction,
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
ParameterSetName = 'EndFunction'
|
||||||
|
)]
|
||||||
|
[switch]$EndFunction,
|
||||||
|
###
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
ParameterSetName = 'Start'
|
||||||
|
)]
|
||||||
|
[switch]$Start,
|
||||||
|
###
|
||||||
|
[Parameter(
|
||||||
|
Mandatory = $false,
|
||||||
|
ParameterSetName = 'End'
|
||||||
|
)]
|
||||||
|
[string]$OutputPath
|
||||||
|
)
|
||||||
|
begin {
|
||||||
|
$ErrorActionPreference = "SilentlyContinue"
|
||||||
|
# Define variables to hold information about the command that was invoked.
|
||||||
|
$ModuleName = $Script:MyInvocation.MyCommand.Name -replace '\..*'
|
||||||
|
$callStack = Get-PSCallStack
|
||||||
|
if ($callStack.Count -gt 1) {
|
||||||
|
$FuncName = $callStack[1].Command
|
||||||
|
} else {
|
||||||
|
$FuncName = "DirectCall" # Or any other default name you prefer
|
||||||
|
}
|
||||||
|
#Write-Verbose "Funcname Name is $FuncName!" -Verbose
|
||||||
|
$ModuleVer = $MyInvocation.MyCommand.Version.ToString()
|
||||||
|
# Set the error action preference to continue.
|
||||||
|
$ErrorActionPreference = "Continue"
|
||||||
|
}
|
||||||
|
process {
|
||||||
|
try {
|
||||||
|
if (-not $Start -and -not (Test-Path variable:script:LogString)) {
|
||||||
|
throw "The logging variable is not initialized. Please call Write-AuditLog with the -Start switch or ensure $script:LogString is set."
|
||||||
|
}
|
||||||
|
$Function = $($FuncName + '.v' + $ModuleVer)
|
||||||
|
if ($Start) {
|
||||||
|
$script:LogString = @()
|
||||||
|
$Message = '+++ Begin Log | ' + $Function + ' |'
|
||||||
|
}
|
||||||
|
elseif ($BeginFunction) {
|
||||||
|
$Message = '>>> Begin Function Log | ' + $Function + ' |'
|
||||||
|
}
|
||||||
|
$logEntry = [pscustomobject]@{
|
||||||
|
Time = ((Get-Date).ToString('yyyy-MM-dd hh:mmTss'))
|
||||||
|
Module = $ModuleName
|
||||||
|
PSVersion = ($PSVersionTable.PSVersion).ToString()
|
||||||
|
PSEdition = ($PSVersionTable.PSEdition).ToString()
|
||||||
|
IsAdmin = $(Test-IsAdmin)
|
||||||
|
User = "$Env:USERDOMAIN\$Env:USERNAME"
|
||||||
|
HostName = $Env:COMPUTERNAME
|
||||||
|
InvokedBy = $Function
|
||||||
|
Severity = $Severity
|
||||||
|
Message = $Message
|
||||||
|
RunID = -1
|
||||||
|
}
|
||||||
|
if ($BeginFunction) {
|
||||||
|
$maxRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Measure-Object -Property RunID -Maximum).Maximum
|
||||||
|
if ($null -eq $maxRunID) { $maxRunID = -1 }
|
||||||
|
$logEntry.RunID = $maxRunID + 1
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$lastRunID = ($script:LogString | Where-Object { $_.InvokedBy -eq $Function } | Select-Object -Last 1).RunID
|
||||||
|
if ($null -eq $lastRunID) { $lastRunID = 0 }
|
||||||
|
$logEntry.RunID = $lastRunID
|
||||||
|
}
|
||||||
|
if ($EndFunction) {
|
||||||
|
$FunctionStart = "$((($script:LogString | Where-Object {$_.InvokedBy -eq $Function -and $_.RunId -eq $lastRunID } | Sort-Object Time)[0]).Time)"
|
||||||
|
$startTime = ([DateTime]::ParseExact("$FunctionStart", 'yyyy-MM-dd hh:mmTss', $null))
|
||||||
|
$endTime = Get-Date
|
||||||
|
$timeTaken = $endTime - $startTime
|
||||||
|
$Message = '<<< End Function Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
|
||||||
|
$logEntry.Message = $Message
|
||||||
|
}
|
||||||
|
elseif ($End) {
|
||||||
|
$startTime = ([DateTime]::ParseExact($($script:LogString[0].Time), 'yyyy-MM-dd hh:mmTss', $null))
|
||||||
|
$endTime = Get-Date
|
||||||
|
$timeTaken = $endTime - $startTime
|
||||||
|
$Message = '--- End Log | ' + $Function + ' | Runtime: ' + "$($timeTaken.Minutes) min $($timeTaken.Seconds) sec"
|
||||||
|
$logEntry.Message = $Message
|
||||||
|
}
|
||||||
|
$script:LogString += $logEntry
|
||||||
|
switch ($Severity) {
|
||||||
|
'Warning' {
|
||||||
|
Write-Warning ('[WARNING] ! ' + $Message)
|
||||||
|
$UserInput = Read-Host "Warning encountered! Do you want to continue? (Y/N)"
|
||||||
|
if ($UserInput -eq 'N') {
|
||||||
|
throw "Script execution stopped by user."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'Error' { Write-Error ('[ERROR] X - ' + $FuncName + ' ' + $Message) -ErrorAction Continue }
|
||||||
|
'Verbose' { Write-Verbose ('[VERBOSE] ~ ' + $Message) }
|
||||||
|
Default { Write-Information ('[INFO] * ' + $Message) -InformationAction Continue}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "Write-AuditLog encountered an error (process block): $($_)"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
end {
|
||||||
|
try {
|
||||||
|
if ($End) {
|
||||||
|
if (-not [string]::IsNullOrEmpty($OutputPath)) {
|
||||||
|
$script:LogString | Export-Csv -Path $OutputPath -NoTypeInformation
|
||||||
|
Write-Verbose "LogPath: $(Split-Path -Path $OutputPath -Parent)"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw "OutputPath is not specified for End action."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
throw "Error in Write-AuditLog (end block): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
source/Public/Get-AdminRoleUserLicense.ps1
Normal file
88
source/Public/Get-AdminRoleUserLicense.ps1
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API.
|
||||||
|
.DESCRIPTION
|
||||||
|
The Get-AdminRoleUserLicense function connects to Microsoft Graph and retrieves all users who are assigned administrative roles along with their user details and licenses. This function is useful for auditing and compliance checks to ensure that administrators have appropriate licenses and role assignments.
|
||||||
|
.PARAMETER SkipGraphConnection
|
||||||
|
A switch parameter that, when set, skips the connection to Microsoft Graph if already established. This is useful for batch processing or when used within scripts where multiple calls are made and the connection is managed externally.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Get-AdminRoleUserLicense
|
||||||
|
|
||||||
|
This example retrieves all administrative role users along with their licenses by connecting to Microsoft Graph using the default scopes.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Get-AdminRoleUserLicense -SkipGraphConnection
|
||||||
|
|
||||||
|
This example retrieves all administrative role users along with their licenses without attempting to connect to Microsoft Graph, assuming that the connection is already established.
|
||||||
|
.INPUTS
|
||||||
|
None. You cannot pipe objects to Get-AdminRoleUserLicense.
|
||||||
|
.OUTPUTS
|
||||||
|
PSCustomObject
|
||||||
|
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.
|
||||||
|
.LINK
|
||||||
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
|
||||||
|
#>
|
||||||
|
function Get-AdminRoleUserLicense {
|
||||||
|
# Set output type to System.Collections.ArrayList
|
||||||
|
[OutputType([System.Collections.ArrayList])]
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[switch]$SkipGraphConnection
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
if (-not $SkipGraphConnection) {
|
||||||
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
|
||||||
|
}
|
||||||
|
|
||||||
|
$adminRoleUsers = [System.Collections.ArrayList]::new()
|
||||||
|
$userIds = [System.Collections.ArrayList]::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
Process {
|
||||||
|
$adminroles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
|
||||||
|
|
||||||
|
foreach ($role in $adminroles) {
|
||||||
|
$usersInRole = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
|
||||||
|
|
||||||
|
foreach ($user in $usersInRole) {
|
||||||
|
$userDetails = Get-MgUser -UserId $user.PrincipalId -Property "DisplayName, UserPrincipalName, Id, onPremisesSyncEnabled" -ErrorAction SilentlyContinue
|
||||||
|
|
||||||
|
if ($userDetails) {
|
||||||
|
[void]($userIds.Add($user.PrincipalId))
|
||||||
|
[void](
|
||||||
|
$adminRoleUsers.Add(
|
||||||
|
[PSCustomObject]@{
|
||||||
|
RoleName = $role.DisplayName
|
||||||
|
UserName = $userDetails.DisplayName
|
||||||
|
UserPrincipalName = $userDetails.UserPrincipalName
|
||||||
|
UserId = $userDetails.Id
|
||||||
|
HybridUser = $userDetails.onPremisesSyncEnabled
|
||||||
|
Licenses = $null # Initialize as $null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($userId in $userIds.ToArray() | Select-Object -Unique) {
|
||||||
|
$licenses = Get-MgUserLicenseDetail -UserId $userId -ErrorAction SilentlyContinue
|
||||||
|
if ($licenses) {
|
||||||
|
$licenseList = ($licenses.SkuPartNumber -join '|')
|
||||||
|
$adminRoleUsers.ToArray() | Where-Object { $_.UserId -eq $userId } | ForEach-Object {
|
||||||
|
$_.Licenses = $licenseList
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
End {
|
||||||
|
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
||||||
|
Disconnect-MgGraph | Out-Null
|
||||||
|
return $adminRoleUsers
|
||||||
|
}
|
||||||
|
}
|
@@ -4,9 +4,9 @@
|
|||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
|
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
|
||||||
.PARAMETER TenantAdminUrl
|
.PARAMETER TenantAdminUrl
|
||||||
The URL of the tenant admin. This parameter is mandatory.
|
The URL of the tenant admin. If not specified, none of the SharePoint Online tests will run.
|
||||||
.PARAMETER DomainName
|
.PARAMETER M365DomainForPWPolicyTest
|
||||||
The domain name of the Microsoft 365 environment. This parameter is mandatory.
|
The domain name of the Microsoft 365 environment to test. This parameter is not mandatory and by default it will pass/fail all found domains as a group if a specific domain is not specified.
|
||||||
.PARAMETER ELevel
|
.PARAMETER ELevel
|
||||||
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
|
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
|
||||||
.PARAMETER ProfileLevel
|
.PARAMETER ProfileLevel
|
||||||
@@ -28,70 +28,127 @@
|
|||||||
.PARAMETER NoModuleCheck
|
.PARAMETER NoModuleCheck
|
||||||
If specified, the cmdlet will not check for the presence of required modules.
|
If specified, the cmdlet will not check for the presence of required modules.
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
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
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
||||||
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
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
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -IncludeIG1
|
||||||
|
|
||||||
Performs an audit including all tests where IG1 is true.
|
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
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
||||||
|
|
||||||
Performs an audit while excluding specific recommendations 1.1.3 and 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
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com"
|
||||||
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
||||||
|
|
||||||
Captures the audit results into a variable and exports them to a CSV file.
|
Captures the audit results into a variable and exports them to a CSV file.
|
||||||
|
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
|
.INPUTS
|
||||||
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
||||||
.OUTPUTS
|
.OUTPUTS
|
||||||
CISAuditResult[]
|
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
|
.NOTES
|
||||||
- This module is based on CIS benchmarks.
|
- This module is based on CIS benchmarks.
|
||||||
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
|
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
|
||||||
- Modifications and sharing are allowed under the same license.
|
- Modifications and sharing are allowed under the same license.
|
||||||
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
|
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
|
||||||
|
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
|
||||||
.LINK
|
.LINK
|
||||||
Online Version: [GitHub Repository URL]
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||||
#>
|
#>
|
||||||
|
|
||||||
function Invoke-M365SecurityAudit {
|
function Invoke-M365SecurityAudit {
|
||||||
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
||||||
[OutputType([CISAuditResult[]])]
|
[OutputType([CISAuditResult[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
|
||||||
|
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
|
||||||
[string]$TenantAdminUrl,
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration policy when '1.3.1' is included in the tests to be run. The domain name of your organization, e.g., 'example.com'.")]
|
||||||
[string]$DomainName,
|
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
|
||||||
|
[string]$M365DomainForPWPolicyTest,
|
||||||
|
|
||||||
# E-Level with optional ProfileLevel selection
|
# E-Level with optional ProfileLevel selection
|
||||||
[Parameter(ParameterSetName = 'ELevelFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
|
||||||
[ValidateSet('E3', 'E5')]
|
[ValidateSet('E3', 'E5')]
|
||||||
[string]$ELevel,
|
[string]$ELevel,
|
||||||
|
|
||||||
[Parameter(ParameterSetName = 'ELevelFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
|
||||||
[ValidateSet('L1', 'L2')]
|
[ValidateSet('L1', 'L2')]
|
||||||
[string]$ProfileLevel,
|
[string]$ProfileLevel,
|
||||||
|
|
||||||
# IG Filters, one at a time
|
# IG Filters, one at a time
|
||||||
[Parameter(ParameterSetName = 'IG1Filter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')]
|
||||||
[switch]$IncludeIG1,
|
[switch]$IncludeIG1,
|
||||||
|
|
||||||
[Parameter(ParameterSetName = 'IG2Filter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')]
|
||||||
[switch]$IncludeIG2,
|
[switch]$IncludeIG2,
|
||||||
|
|
||||||
[Parameter(ParameterSetName = 'IG3Filter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')]
|
||||||
[switch]$IncludeIG3,
|
[switch]$IncludeIG3,
|
||||||
|
|
||||||
# Inclusion of specific recommendation numbers
|
# Inclusion of specific recommendation numbers
|
||||||
[Parameter(ParameterSetName = 'RecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
||||||
[ValidateSet(
|
[ValidateSet(
|
||||||
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
||||||
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
||||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
@@ -102,9 +159,9 @@ function Invoke-M365SecurityAudit {
|
|||||||
[string[]]$IncludeRecommendation,
|
[string[]]$IncludeRecommendation,
|
||||||
|
|
||||||
# Exclusion of specific recommendation numbers
|
# Exclusion of specific recommendation numbers
|
||||||
[Parameter(ParameterSetName = 'SkipRecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
||||||
[ValidateSet(
|
[ValidateSet(
|
||||||
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
||||||
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
||||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
@@ -120,139 +177,117 @@ function Invoke-M365SecurityAudit {
|
|||||||
[switch]$NoModuleCheck
|
[switch]$NoModuleCheck
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Begin {
|
Begin {
|
||||||
if ($script:MaximumFunctionCount -lt 8192) {
|
if ($script:MaximumFunctionCount -lt 8192) {
|
||||||
$script:MaximumFunctionCount = 8192
|
$script:MaximumFunctionCount = 8192
|
||||||
}
|
}
|
||||||
# Ensure required modules are installed
|
# Ensure required modules are installed
|
||||||
# Define the required modules and versions in a hashtable
|
$requiredModules = Get-RequiredModule -AuditFunction
|
||||||
if (!($NoModuleCheck)) {
|
|
||||||
$requiredModules = @(
|
# Format the required modules list
|
||||||
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" },
|
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
|
||||||
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" },
|
# Check and install required modules if necessary
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" },
|
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules: $requiredModulesFormatted", "Check")) {
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Groups" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "DirectoryObjects" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Domains" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Reports" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Mail" },
|
|
||||||
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000" },
|
|
||||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0" }
|
|
||||||
)
|
|
||||||
foreach ($module in $requiredModules) {
|
foreach ($module in $requiredModules) {
|
||||||
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Loop through each required module and assert its availability
|
|
||||||
|
|
||||||
# Establishing connections
|
|
||||||
#if (!($DoNotConnect -or $DoNotTest)) {
|
|
||||||
# Establishing connections
|
|
||||||
if (!($DoNotConnect)) {
|
|
||||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
# Load test definitions from CSV
|
# Load test definitions from CSV
|
||||||
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
||||||
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
|
# Load the Test Definitions into the script scope for use in other functions
|
||||||
|
$script:TestDefinitionsObject = $testDefinitions
|
||||||
|
|
||||||
# Apply filters based on parameter sets
|
# Apply filters based on parameter sets
|
||||||
switch ($PSCmdlet.ParameterSetName) {
|
$params = @{
|
||||||
'ELevelFilter' {
|
TestDefinitions = $testDefinitions
|
||||||
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
|
ParameterSetName = $PSCmdlet.ParameterSetName
|
||||||
$testDefinitions = $testDefinitions | Where-Object {
|
ELevel = $ELevel
|
||||||
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
|
ProfileLevel = $ProfileLevel
|
||||||
}
|
IncludeRecommendation = $IncludeRecommendation
|
||||||
|
SkipRecommendation = $SkipRecommendation
|
||||||
|
}
|
||||||
|
$testDefinitions = Get-TestDefinitionsObject @params
|
||||||
|
# Extract unique connections needed
|
||||||
|
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
||||||
|
if ($requiredConnections -contains 'SPO') {
|
||||||
|
if (-not $TenantAdminUrl) {
|
||||||
|
$requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' }
|
||||||
|
$testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' }
|
||||||
|
if ($null -eq $testDefinitions) {
|
||||||
|
throw "No tests to run as no SharePoint Online tests are available."
|
||||||
}
|
}
|
||||||
elseif ($null -ne $ELevel) {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object {
|
|
||||||
$_.ELevel -eq $ELevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ($null -ne $ProfileLevel) {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object {
|
|
||||||
$_.ProfileLevel -eq $ProfileLevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'IG1Filter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
|
|
||||||
}
|
|
||||||
'IG2Filter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
|
|
||||||
}
|
|
||||||
'IG3Filter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
|
|
||||||
}
|
|
||||||
'RecFilter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
|
|
||||||
}
|
|
||||||
'SkipRecFilter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# End switch ($PSCmdlet.ParameterSetName)
|
|
||||||
|
|
||||||
# Determine which test files to load based on filtering
|
# Determine which test files to load based on filtering
|
||||||
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
||||||
|
|
||||||
# Display the tests that would be loaded if the function is called with -WhatIf
|
|
||||||
|
|
||||||
Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:"
|
Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:"
|
||||||
$testsToLoad | ForEach-Object { Write-Verbose " $_" }
|
$testsToLoad | ForEach-Object { Write-Verbose " $_" }
|
||||||
|
# Initialize a collection to hold failed test details
|
||||||
|
$script:FailedTests = [System.Collections.ArrayList]::new()
|
||||||
} # End Begin
|
} # End Begin
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
$allAuditResults = @() # Initialize a collection to hold all results
|
$allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
|
||||||
|
|
||||||
# Dynamically dot-source the test scripts
|
# Dynamically dot-source the test scripts
|
||||||
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
|
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
|
||||||
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
|
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
|
||||||
Where-Object { $testsToLoad -contains $_.BaseName }
|
Where-Object { $testsToLoad -contains $_.BaseName }
|
||||||
|
|
||||||
|
$totalTests = $testFiles.Count
|
||||||
|
$currentTestIndex = 0
|
||||||
|
|
||||||
|
# Establishing connections if required
|
||||||
|
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
||||||
|
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
|
||||||
|
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" -InformationAction Continue
|
||||||
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Write-Information "A total of $($totalTests) tests were selected to run..." -InformationAction Continue
|
||||||
# Import the test functions
|
# Import the test functions
|
||||||
$testFiles | ForEach-Object {
|
$testFiles | ForEach-Object {
|
||||||
|
$currentTestIndex++
|
||||||
|
Write-Progress -Activity "Loading Test Scripts" -Status "Loading $($currentTestIndex) of $($totalTests): $($_.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
||||||
Try {
|
Try {
|
||||||
|
# Dot source the test function
|
||||||
. $_.FullName
|
. $_.FullName
|
||||||
}
|
}
|
||||||
Catch {
|
Catch {
|
||||||
|
# Log the error and add the test to the failed tests collection
|
||||||
Write-Error "Failed to load test function $($_.Name): $_"
|
Write-Error "Failed to load test function $($_.Name): $_"
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$currentTestIndex = 0
|
||||||
# Execute each test function from the prepared list
|
# Execute each test function from the prepared list
|
||||||
foreach ($testFunction in $testFiles) {
|
foreach ($testFunction in $testFiles) {
|
||||||
|
$currentTestIndex++
|
||||||
|
Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
||||||
$functionName = $testFunction.BaseName
|
$functionName = $testFunction.BaseName
|
||||||
$functionCmd = Get-Command -Name $functionName
|
|
||||||
|
|
||||||
# Check if the test function needs DomainName parameter
|
|
||||||
$paramList = @{}
|
|
||||||
if ('DomainName' -in $functionCmd.Parameters.Keys) {
|
|
||||||
$paramList.DomainName = $DomainName
|
|
||||||
}
|
|
||||||
|
|
||||||
# Use splatting to pass parameters
|
|
||||||
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
|
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
|
||||||
Write-Host "Running $functionName..."
|
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $M365DomainForPWPolicyTest
|
||||||
$result = & $functionName @paramList
|
# Add the result to the collection
|
||||||
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
[void]$allAuditResults.Add($auditResult)
|
||||||
$allAuditResults += $result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
End {
|
End {
|
||||||
# Return all collected audit results
|
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Disconnect")) {
|
||||||
return $allAuditResults
|
|
||||||
# Check if the Disconnect switch is present
|
|
||||||
if (!($DoNotDisconnect)) {
|
|
||||||
# Clean up sessions
|
# Clean up sessions
|
||||||
Disconnect-M365Suite
|
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
||||||
|
}
|
||||||
|
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
|
||||||
|
return $allAuditResults.ToArray() | Sort-Object -Property Rec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
90
source/Public/Sync-CISExcelAndCsvData.ps1
Normal file
90
source/Public/Sync-CISExcelAndCsvData.ps1
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Synchronizes data between an Excel file and either a CSV file or an output object from Invoke-M365SecurityAudit, and optionally updates the Excel worksheet.
|
||||||
|
.DESCRIPTION
|
||||||
|
The Sync-CISExcelAndCsvData function merges data from a specified Excel file with data from either a CSV file or an output object from Invoke-M365SecurityAudit based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file or audit results while preserving the original formatting and structure of the Excel worksheet.
|
||||||
|
.PARAMETER ExcelPath
|
||||||
|
The path to the Excel file that contains the original data. This parameter is mandatory.
|
||||||
|
.PARAMETER WorksheetName
|
||||||
|
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
|
||||||
|
.PARAMETER CsvPath
|
||||||
|
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory when using the CsvInput parameter set.
|
||||||
|
.PARAMETER AuditResults
|
||||||
|
An array of CISAuditResult objects from Invoke-M365SecurityAudit to be merged with the Excel data. This parameter is mandatory when using the ObjectInput parameter set. It can also accept pipeline input.
|
||||||
|
.PARAMETER SkipUpdate
|
||||||
|
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv"
|
||||||
|
Merges data from 'data.csv' into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
|
||||||
|
Retrieves the merged data object for preview without updating the Excel worksheet.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
|
||||||
|
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults
|
||||||
|
Merges data from the audit results into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
|
||||||
|
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults -SkipUpdate
|
||||||
|
Retrieves the merged data object for preview without updating the Excel worksheet.
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com" | Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet"
|
||||||
|
Pipes the audit results into Sync-CISExcelAndCsvData to merge data into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||||
|
.INPUTS
|
||||||
|
System.String, CISAuditResult[]
|
||||||
|
You can pipe CISAuditResult objects to Sync-CISExcelAndCsvData.
|
||||||
|
.OUTPUTS
|
||||||
|
Object[]
|
||||||
|
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
|
||||||
|
.NOTES
|
||||||
|
- Ensure that the 'ImportExcel' module is installed and up to date.
|
||||||
|
- It is recommended to backup the Excel file before running this script to prevent accidental data loss.
|
||||||
|
- This function is part of the CIS Excel and CSV Data Management Toolkit.
|
||||||
|
.LINK
|
||||||
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
||||||
|
#>
|
||||||
|
function Sync-CISExcelAndCsvData {
|
||||||
|
[OutputType([void], [PSCustomObject[]])]
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[ValidateScript({ Test-Path $_ })]
|
||||||
|
[string]$ExcelPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$WorksheetName,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
|
||||||
|
[ValidateScript({ Test-Path $_ })]
|
||||||
|
[string]$CsvPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput', ValueFromPipeline = $true)]
|
||||||
|
[CISAuditResult[]]$AuditResults,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[switch]$SkipUpdate
|
||||||
|
)
|
||||||
|
|
||||||
|
process {
|
||||||
|
# Verify ImportExcel module is available
|
||||||
|
$requiredModules = Get-RequiredModule -SyncFunction
|
||||||
|
foreach ($module in $requiredModules) {
|
||||||
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
||||||
|
}
|
||||||
|
|
||||||
|
# Merge Excel and CSV data or Audit Results
|
||||||
|
if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
|
||||||
|
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
|
||||||
|
} else {
|
||||||
|
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -AuditResults $AuditResults
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output the merged data if the user chooses to skip the update
|
||||||
|
if ($SkipUpdate) {
|
||||||
|
return $mergedData
|
||||||
|
} else {
|
||||||
|
# Update the Excel worksheet with the merged data
|
||||||
|
Update-CISExcelWorksheet -ExcelPath $ExcelPath -WorksheetName $WorksheetName -Data $mergedData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,51 +1,52 @@
|
|||||||
Index,TestFileName,Rec,ELevel,ProfileLevel,IG1,IG2,IG3
|
Index,TestFileName,Rec,RecDescription,ELevel,ProfileLevel,CISControl,CISDescription,IG1,IG2,IG3,Automated,Connection
|
||||||
1,Test-AntiPhishingPolicy.ps1,2.1.7,E5,L1,FALSE,FALSE,TRUE
|
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-AuditDisabledFalse.ps1,6.1.1,E3,L1,TRUE,TRUE,TRUE
|
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-AuditLogSearch.ps1,3.1.1,E3,L1,TRUE,TRUE,TRUE
|
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-BlockChannelEmails.ps1,8.1.2,E3,L1,FALSE,FALSE,FALSE
|
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
|
||||||
5,Test-BlockMailForwarding.ps1,6.2.1,E3,L1,FALSE,FALSE,FALSE
|
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-BlockSharedMailboxSignIn.ps1,1.2.2,E3,L1,FALSE,FALSE,FALSE
|
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-CommonAttachmentFilter.ps1,2.1.2,E3,L1,FALSE,TRUE,TRUE
|
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
|
||||||
8,Test-CustomerLockbox.ps1,1.3.6,E5,L2,FALSE,FALSE,FALSE
|
8,Test-SafeLinksOfficeApps.ps1,2.1.1,Ensure Safe Links for Office Applications is Enabled,E5,L2,10.1,Deploy and Maintain Anti-Malware Software,TRUE,TRUE,TRUE,TRUE,EXO
|
||||||
9,Test-DialInBypassLobby.ps1,8.5.4,E3,L1,FALSE,FALSE,FALSE
|
9,Test-CommonAttachmentFilter.ps1,2.1.2,Ensure the Common Attachment Types Filter is enabled,E3,L1,9.6,Block Unnecessary File Types,FALSE,TRUE,TRUE,TRUE,EXO
|
||||||
10,Test-DisallowInfectedFilesDownload.ps1,7.3.1,E5,L2,TRUE,TRUE,TRUE
|
10,Test-NotifyMalwareInternal.ps1,2.1.3,Ensure notifications for internal users sending malware is Enabled,E3,L1,17.5,Assign Key Roles and Responsibilities,FALSE,TRUE,TRUE,TRUE,EXO
|
||||||
11,Test-EnableDKIM.ps1,2.1.9,E3,L1,FALSE,TRUE,TRUE
|
11,Test-SafeAttachmentsPolicy.ps1,2.1.4,Ensure Safe Attachments policy is enabled,E5,L2,9.7,Deploy and Maintain Email Server Anti-Malware Protections,FALSE,FALSE,TRUE,TRUE,EXO
|
||||||
12,Test-ExternalNoControl.ps1,8.5.7,E3,L1,FALSE,FALSE,FALSE
|
12,Test-SafeAttachmentsTeams.ps1,2.1.5,"Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled",E5,L2,"9.7, 10.1","Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software",TRUE,TRUE,TRUE,TRUE,EXO
|
||||||
13,Test-ExternalSharingCalendars.ps1,1.3.3,E3,L2,FALSE,TRUE,TRUE
|
13,Test-SpamPolicyAdminNotify.ps1,2.1.6,Ensure Exchange Online Spam Policies are set to notify administrators,E3,L1,17.5,Assign Key Roles and Responsibilities,FALSE,TRUE,TRUE,TRUE,EXO
|
||||||
14,Test-GlobalAdminsCount.ps1,1.1.3,E3,L1,TRUE,TRUE,TRUE
|
14,Test-AntiPhishingPolicy.ps1,2.1.7,Ensure that an anti-phishing policy has been created,E5,L1,9.7,Deploy and Maintain Email Server Anti-Malware Protections,FALSE,FALSE,TRUE,TRUE,EXO
|
||||||
15,Test-GuestAccessExpiration.ps1,7.2.9,E3,L1,FALSE,FALSE,FALSE
|
15,Test-EnableDKIM.ps1,2.1.9,Ensure that DKIM is enabled for all Exchange Online Domains,E3,L1,9.5,Implement DMARC,FALSE,TRUE,TRUE,TRUE,EXO
|
||||||
16,Test-IdentifyExternalEmail.ps1,6.2.3,E3,L1,FALSE,FALSE,FALSE
|
16,Test-AuditLogSearch.ps1,3.1.1,Ensure Microsoft 365 audit log search is Enabled,E3,L1,8.2,Collect Audit Logs,TRUE,TRUE,TRUE,TRUE,EXO
|
||||||
17,Test-LinkSharingRestrictions.ps1,7.2.7,E3,L1,TRUE,TRUE,TRUE
|
17,Test-RestrictTenantCreation.ps1,5.1.2.3,Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes',E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Graph
|
||||||
18,Test-MailboxAuditingE3.ps1,6.1.2,E3,L1,TRUE,TRUE,TRUE
|
18,Test-PasswordHashSync.ps1,5.1.8.1,Ensure password hash sync is enabled for hybrid deployments,E3,L1,6.7,Centralize Access Control,FALSE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||||
19,Test-MailboxAuditingE5.ps1,6.1.3,E5,L1,TRUE,TRUE,TRUE
|
19,Test-AuditDisabledFalse.ps1,6.1.1,Ensure 'AuditDisabled' organizationally is set to 'False',E3,L1,8.2,Collect Audit Logs,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||||
20,Test-MailTipsEnabled.ps1,6.5.2,E3,L2,FALSE,FALSE,FALSE
|
20,Test-MailboxAuditingE3.ps1,6.1.2,Ensure mailbox auditing for Office E3 users is Enabled,E3,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO | Microsoft Graph
|
||||||
21,Test-ManagedApprovedPublicGroups.ps1,1.2.1,E3,L2,TRUE,TRUE,TRUE
|
21,Test-MailboxAuditingE5.ps1,6.1.3,Ensure mailbox auditing for Office E5 users is Enabled,E5,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO | Microsoft Graph
|
||||||
22,Test-MeetingChatNoAnonymous.ps1,8.5.5,E3,L1,FALSE,FALSE,FALSE
|
22,Test-BlockMailForwarding.ps1,6.2.1,Ensure all forms of mail forwarding are blocked and/or disabled,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||||
23,Test-ModernAuthExchangeOnline.ps1,6.5.1,E3,L1,FALSE,TRUE,TRUE
|
23,Test-NoWhitelistDomains.ps1,6.2.2,Ensure mail transport rules do not whitelist specific domains,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||||
24,Test-ModernAuthSharePoint.ps1,7.2.1,E3,L1,FALSE,TRUE,TRUE
|
24,Test-IdentifyExternalEmail.ps1,6.2.3,Ensure email from external senders is identified,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||||
25,Test-NoAnonymousMeetingJoin.ps1,8.5.1,E3,L2,FALSE,FALSE,FALSE
|
25,Test-RestrictOutlookAddins.ps1,6.3.1,Ensure users installing Outlook add-ins is not allowed,E3,L2,9.4,Restrict Unnecessary or Unauthorized Browser and Email Client Extensions,FALSE,TRUE,TRUE,TRUE,EXO
|
||||||
26,Test-NoAnonymousMeetingStart.ps1,8.5.2,E3,L1,FALSE,FALSE,FALSE
|
26,Test-ModernAuthExchangeOnline.ps1,6.5.1,Ensure modern authentication for Exchange Online is enabled (Automated),E3,L1,3.1,Encrypt Sensitive Data in Transit,FALSE,TRUE,TRUE,TRUE,EXO
|
||||||
27,Test-NotifyMalwareInternal.ps1,2.1.3,E3,L1,FALSE,TRUE,TRUE
|
27,Test-MailTipsEnabled.ps1,6.5.2,Ensure MailTips are enabled for end users,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||||
28,Test-NoWhitelistDomains.ps1,6.2.2,E3,L1,FALSE,FALSE,FALSE
|
28,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,Ensure additional storage providers are restricted in Outlook on the web,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,EXO
|
||||||
29,Test-OneDriveContentRestrictions.ps1,7.2.4,E3,L2,TRUE,TRUE,TRUE
|
29,Test-ModernAuthSharePoint.ps1,7.2.1,Modern Authentication for SharePoint Applications,E3,L1,3.1,Encrypt Sensitive Data in Transit,FALSE,TRUE,TRUE,TRUE,SPO
|
||||||
30,Test-OneDriveSyncRestrictions.ps1,7.3.2,E3,L2,FALSE,FALSE,FALSE
|
30,Test-SharePointAADB2B.ps1,7.2.2,Ensure reauthentication with verification code is restricted,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
|
||||||
31,Test-OrganizersPresent.ps1,8.5.6,E3,L1,FALSE,FALSE,FALSE
|
31,Test-RestrictExternalSharing.ps1,7.2.3,Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled,E3,L1,0,Explicitly Not Mapped,TRUE,TRUE,TRUE,TRUE,SPO
|
||||||
32,Test-OrgOnlyBypassLobby.ps1,8.5.3,E3,L1,FALSE,FALSE,TRUE
|
32,Test-OneDriveContentRestrictions.ps1,7.2.4,Ensure external content sharing is restricted,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
|
||||||
33,Test-PasswordHashSync.ps1,5.1.8.1,E3,L1,FALSE,TRUE,TRUE
|
33,Test-SharePointGuestsItemSharing.ps1,7.2.5,Ensure OneDrive content sharing is restricted,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
|
||||||
34,Test-PasswordNeverExpirePolicy.ps1,1.3.1,E3,L1,TRUE,TRUE,TRUE
|
34,Test-SharePointExternalSharingDomains.ps1,7.2.6,Ensure that SharePoint guest users cannot share items they don't own,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
|
||||||
35,Test-ReauthWithCode.ps1,7.2.10,E3,L1,FALSE,FALSE,FALSE
|
35,Test-LinkSharingRestrictions.ps1,7.2.7,Ensure SharePoint external sharing is managed through domain whitelist/blacklists,E3,L1,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,SPO
|
||||||
36,Test-ReportSecurityInTeams.ps1,8.6.1,E3,L1,FALSE,FALSE,FALSE
|
36,Test-GuestAccessExpiration.ps1,7.2.9,Ensure link sharing is restricted in SharePoint and OneDrive,E3,L1,3.3,Configure Data Access Control Lists,FALSE,FALSE,FALSE,TRUE,SPO
|
||||||
37,Test-RestrictCustomScripts.ps1,7.3.4,E3,L1,FALSE,FALSE,TRUE
|
37,Test-ReauthWithCode.ps1,7.2.10,Ensure guest access to a site or OneDrive will expire automatically,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
|
||||||
38,Test-RestrictExternalSharing.ps1,7.2.3,E3,L1,TRUE,TRUE,TRUE
|
38,Test-DisallowInfectedFilesDownload.ps1,7.3.1,Ensure Office 365 SharePoint infected files are disallowed for download,E5,L2,10.1,Deploy and Maintain Anti-Malware Software,TRUE,TRUE,TRUE,TRUE,SPO
|
||||||
39,Test-RestrictOutlookAddins.ps1,6.3.1,E3,L2,FALSE,TRUE,TRUE
|
39,Test-OneDriveSyncRestrictions.ps1,7.3.2,Ensure OneDrive sync is restricted for unmanaged devices,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,SPO
|
||||||
40,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,E3,L2,TRUE,TRUE,TRUE
|
40,Test-RestrictCustomScripts.ps1,7.3.4,Ensure custom script execution is restricted on site collections,E3,L1,2.7,Allowlist Authorized Scripts,FALSE,FALSE,TRUE,TRUE,SPO
|
||||||
41,Test-RestrictTenantCreation.ps1,5.1.2.3,E3,L1,FALSE,FALSE,FALSE
|
41,Test-TeamsExternalFileSharing.ps1,8.1.1,Ensure external file sharing in Teams is enabled for only approved cloud storage services,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Teams
|
||||||
42,Test-SafeAttachmentsPolicy.ps1,2.1.4,E5,L2,FALSE,FALSE,TRUE
|
42,Test-BlockChannelEmails.ps1,8.1.2,Ensure users can't send emails to a channel email address,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
43,Test-SafeAttachmentsTeams.ps1,2.1.5,E5,L2,TRUE,TRUE,TRUE
|
43,Test-TeamsExternalAccess.ps1,8.2.1,Ensure 'external access' is restricted in the Teams admin center,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
44,Test-SafeLinksOfficeApps.ps1,2.1.1,E5,L2,TRUE,TRUE,TRUE
|
44,Test-NoAnonymousMeetingJoin.ps1,8.5.1,Ensure anonymous users can't join a meeting,E3,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
45,Test-SharePointAADB2B.ps1,7.2.2,E3,L1,FALSE,FALSE,FALSE
|
45,Test-NoAnonymousMeetingStart.ps1,8.5.2,Ensure anonymous users and dial-in callers can't start a meeting,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
46,Test-SharePointExternalSharingDomains.ps1,7.2.6,E3,L2,TRUE,TRUE,TRUE
|
46,Test-OrgOnlyBypassLobby.ps1,8.5.3,Ensure only people in my org can bypass the lobby,E3,L1,6.8,Define and Maintain Role-Based Access Control,FALSE,FALSE,TRUE,TRUE,Microsoft Teams
|
||||||
47,Test-SharePointGuestsItemSharing.ps1,7.2.5,E3,L2,TRUE,TRUE,TRUE
|
47,Test-DialInBypassLobby.ps1,8.5.4,Ensure users dialing in can't bypass the lobby,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
48,Test-SpamPolicyAdminNotify.ps1,2.1.6,E3,L1,FALSE,TRUE,TRUE
|
48,Test-MeetingChatNoAnonymous.ps1,8.5.5,Ensure meeting chat does not allow anonymous users,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
49,Test-TeamsExternalAccess.ps1,8.2.1,E3,L2,FALSE,FALSE,FALSE
|
49,Test-OrganizersPresent.ps1,8.5.6,Ensure only organizers and co-organizers can present,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
50,Test-TeamsExternalFileSharing.ps1,8.1.1,E3,L2,TRUE,TRUE,TRUE
|
50,Test-ExternalNoControl.ps1,8.5.7,Ensure external participants can't give or request control,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams
|
||||||
|
51,Test-ReportSecurityInTeams.ps1,8.6.1,Ensure users can report security concerns in Teams,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Teams | EXO
|
||||||
|
|
113
source/tests/Test-AdministrativeAccountCompliance.ps1
Normal file
113
source/tests/Test-AdministrativeAccountCompliance.ps1
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
function Test-AdministrativeAccountCompliance {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
|
param (
|
||||||
|
# Parameters can be added if needed
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
|
||||||
|
$recnum = "1.1.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
try {
|
||||||
|
# Retrieve all necessary data outside the loops
|
||||||
|
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
|
||||||
|
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment
|
||||||
|
$principalIds = $roleAssignments.PrincipalId | Select-Object -Unique
|
||||||
|
|
||||||
|
# Fetch user details using filter
|
||||||
|
$userDetailsList = @{}
|
||||||
|
$licensesList = @{}
|
||||||
|
|
||||||
|
$userDetails = Get-MgUser -Filter "id in ('$($principalIds -join "','")')" -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
|
||||||
|
foreach ($user in $userDetails) {
|
||||||
|
$userDetailsList[$user.Id] = $user
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fetch user licenses for each unique principal ID
|
||||||
|
foreach ($principalId in $principalIds) {
|
||||||
|
$licensesList[$principalId] = Get-MgUserLicenseDetail -UserId $principalId -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
$adminRoleUsers = @()
|
||||||
|
|
||||||
|
foreach ($role in $adminRoles) {
|
||||||
|
foreach ($assignment in $roleAssignments | Where-Object { $_.RoleDefinitionId -eq $role.Id }) {
|
||||||
|
$userDetails = $userDetailsList[$assignment.PrincipalId]
|
||||||
|
if ($userDetails) {
|
||||||
|
$licenses = $licensesList[$assignment.PrincipalId]
|
||||||
|
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
|
||||||
|
|
||||||
|
$adminRoleUsers += [PSCustomObject]@{
|
||||||
|
UserName = $userDetails.UserPrincipalName
|
||||||
|
RoleName = $role.DisplayName
|
||||||
|
UserId = $userDetails.Id
|
||||||
|
HybridUser = $userDetails.OnPremisesSyncEnabled
|
||||||
|
Licenses = $licenseString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$uniqueAdminRoleUsers = $adminRoleUsers | Group-Object -Property UserName | ForEach-Object {
|
||||||
|
$first = $_.Group | Select-Object -First 1
|
||||||
|
$roles = ($_.Group.RoleName -join ', ')
|
||||||
|
$licenses = (($_.Group | Select-Object -ExpandProperty Licenses) -join ',').Split(',') | Select-Object -Unique
|
||||||
|
|
||||||
|
$first | Select-Object UserName, UserId, HybridUser, @{Name = 'Roles'; Expression = { $roles } }, @{Name = 'Licenses'; Expression = { $licenses -join '|' } }
|
||||||
|
}
|
||||||
|
|
||||||
|
$nonCompliantUsers = $uniqueAdminRoleUsers | Where-Object {
|
||||||
|
$_.HybridUser -or
|
||||||
|
-not ($_.Licenses -split '\|' | Where-Object { $validLicenses -contains $_ })
|
||||||
|
}
|
||||||
|
|
||||||
|
$failureReasons = $nonCompliantUsers | ForEach-Object {
|
||||||
|
$accountType = if ($_.HybridUser) { "Hybrid" } else { "Cloud-Only" }
|
||||||
|
$missingLicenses = $validLicenses | Where-Object { $_ -notin ($_.Licenses -split '\|') }
|
||||||
|
"$($_.UserName)|$($_.Roles)|$accountType|$($missingLicenses -join ',')"
|
||||||
|
}
|
||||||
|
$failureReasons = $failureReasons -join "`n"
|
||||||
|
|
||||||
|
$details = if ($nonCompliantUsers) {
|
||||||
|
"Non-compliant accounts: `nUsername | Roles | HybridStatus | Missing Licence`n$failureReasons"
|
||||||
|
} else {
|
||||||
|
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$failureReason = if ($nonCompliantUsers) {
|
||||||
|
"Non-Compliant Accounts: $($nonCompliantUsers.Count)`nDetails:`n" + ($nonCompliantUsers | ForEach-Object { $_.UserName }) -join "`n"
|
||||||
|
} else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $nonCompliantUsers.Count -eq 0
|
||||||
|
$status = if ($result) { 'Pass' } else { 'Fail' }
|
||||||
|
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = $status
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReason
|
||||||
|
}
|
||||||
|
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end {
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
@@ -1,59 +1,96 @@
|
|||||||
function Test-AntiPhishingPolicy {
|
function Test-AntiPhishingPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
#$auditResults = @()
|
||||||
|
$recnum = "2.1.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
|
||||||
# Retrieve and validate the anti-phishing policies
|
try {
|
||||||
$antiPhishPolicies = Get-AntiPhishPolicy
|
# 2.1.7 Ensure that an anti-phishing policy has been created
|
||||||
$validatedPolicies = $antiPhishPolicies | Where-Object {
|
|
||||||
$_.Enabled -eq $true -and
|
# Retrieve and validate the anti-phishing policies
|
||||||
$_.PhishThresholdLevel -ge 2 -and
|
$antiPhishPolicies = Get-AntiPhishPolicy
|
||||||
$_.EnableMailboxIntelligenceProtection -eq $true -and
|
$validatedPolicies = $antiPhishPolicies | Where-Object {
|
||||||
$_.EnableMailboxIntelligence -eq $true -and
|
$_.Enabled -eq $true -and
|
||||||
$_.EnableSpoofIntelligence -eq $true
|
$_.PhishThresholdLevel -ge 2 -and
|
||||||
|
$_.EnableMailboxIntelligenceProtection -eq $true -and
|
||||||
|
$_.EnableMailboxIntelligence -eq $true -and
|
||||||
|
$_.EnableSpoofIntelligence -eq $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if there is at least one policy that meets the requirements
|
||||||
|
$nonCompliantItems = $antiPhishPolicies | Where-Object {
|
||||||
|
$_.Enabled -ne $true -or
|
||||||
|
$_.PhishThresholdLevel -lt 2 -or
|
||||||
|
$_.EnableMailboxIntelligenceProtection -ne $true -or
|
||||||
|
$_.EnableMailboxIntelligence -ne $true -or
|
||||||
|
$_.EnableSpoofIntelligence -ne $true
|
||||||
|
}
|
||||||
|
$compliantItems = $validatedPolicies
|
||||||
|
$isCompliant = $compliantItems.Count -gt 0
|
||||||
|
|
||||||
|
# Prepare failure reasons for non-compliant items
|
||||||
|
$nonCompliantNames = $nonCompliantItems | ForEach-Object { $_.Name }
|
||||||
|
$failureReasons = if ($nonCompliantNames.Count -gt 0) {
|
||||||
|
"Reason: Does not meet one or more compliance criteria.`nNon-compliant Policies:`n" + ($nonCompliantNames -join "`n")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare details for non-compliant items
|
||||||
|
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
|
||||||
|
"Policy: $($_.Name)"
|
||||||
|
}
|
||||||
|
$nonCompliantDetails = $nonCompliantDetails -join "`n"
|
||||||
|
|
||||||
|
# Prepare details based on compliance
|
||||||
|
$details = if ($nonCompliantItems) {
|
||||||
|
"Non-Compliant Items: $($nonCompliantItems.Count)`nDetails:`n$nonCompliantDetails"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Compliant Items: $($compliantItems.Count)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parameter splat for Initialize-CISAuditResult function
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $nonCompliantItems.Count -eq 0
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Check if there is at least one policy that meets the requirements
|
# Retrieve the description from the test definitions
|
||||||
$isCompliant = $validatedPolicies.Count -gt 0
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Prepare failure details if policies are not compliant
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$failureDetails = if (-not $isCompliant) {
|
|
||||||
"No anti-phishing policy is fully compliant with CIS benchmark requirements."
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
} else {
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
"Compliant Anti-Phish Policy Names: " + ($validatedPolicies.Name -join ', ')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E5"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "2.1.7"
|
|
||||||
$auditResult.RecDescription = "Ensure that an anti-phishing policy has been created"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "9.7"
|
|
||||||
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $isCompliant
|
|
||||||
$auditResult.Details = $failureDetails
|
|
||||||
$auditResult.FailureReason = if (-not $isCompliant) { "Anti-phishing policies do not meet CIS benchmark requirements." } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,68 @@
|
|||||||
function Test-AuditDisabledFalse {
|
function Test-AuditDisabledFalse {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
|
# Aligned
|
||||||
param (
|
param (
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
|
||||||
# Pass if AuditDisabled is False. Fail otherwise.
|
|
||||||
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
|
||||||
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
try {
|
||||||
$auditResult = [CISAuditResult]::new()
|
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
||||||
$auditResult.Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "6.1.1"
|
|
||||||
$auditResult.RecDescription = "Ensure 'AuditDisabled' organizationally is set to 'False'"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "8.2"
|
|
||||||
$auditResult.CISDescription = "Collect Audit Logs"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $auditNotDisabled
|
|
||||||
$auditResult.Details = if ($auditNotDisabled) { "Audit is not disabled organizationally" } else { "Audit is disabled organizationally" }
|
|
||||||
$auditResult.FailureReason = if (-not $auditNotDisabled) { "AuditDisabled is set to True" } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Retrieve the AuditDisabled configuration
|
||||||
|
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
||||||
|
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $auditNotDisabled) {
|
||||||
|
"AuditDisabled is set to True"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($auditNotDisabled) {
|
||||||
|
"Audit is not disabled organizationally"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Audit is disabled organizationally"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $auditNotDisabled
|
||||||
|
Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,68 @@
|
|||||||
function Test-AuditLogSearch {
|
function Test-AuditLogSearch {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "3.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
|
||||||
# Pass if UnifiedAuditLogIngestionEnabled is True. Fail otherwise.
|
|
||||||
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
|
||||||
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
try {
|
||||||
$auditResult = [CISAuditResult]::new()
|
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
||||||
$auditResult.Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "3.1.1"
|
|
||||||
$auditResult.RecDescription = "Ensure Microsoft 365 audit log search is Enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "8.2"
|
|
||||||
$auditResult.CISDescription = "Collect Audit Logs"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $auditLogResult
|
|
||||||
$auditResult.Details = "UnifiedAuditLogIngestionEnabled: $($auditLogConfig.UnifiedAuditLogIngestionEnabled)"
|
|
||||||
$auditResult.FailureReason = if (-not $auditLogResult) { "Audit log search is not enabled" } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Retrieve the audit log configuration
|
||||||
|
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
||||||
|
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $auditLogResult) {
|
||||||
|
"Audit log search is not enabled"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($auditLogResult) {
|
||||||
|
"UnifiedAuditLogIngestionEnabled: True"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"UnifiedAuditLogIngestionEnabled: False"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $auditLogResult
|
||||||
|
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,44 +1,68 @@
|
|||||||
function Test-BlockChannelEmails {
|
function Test-BlockChannelEmails {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added here if needed
|
# Parameters can be added here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
||||||
|
|
||||||
|
# Retrieve Teams client configuration
|
||||||
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
|
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
|
||||||
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
|
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($allowEmailIntoChannel) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Emails can be sent to a channel email address"
|
||||||
$auditResult.CISControl = "0.0" # This control is Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.1.2"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the benchmark
|
|
||||||
$auditResult.IG2 = $false # Set based on the benchmark
|
|
||||||
$auditResult.IG3 = $false # Set based on the benchmark
|
|
||||||
$auditResult.RecDescription = "Ensure users can't send emails to a channel email address"
|
|
||||||
$auditResult.Result = -not $allowEmailIntoChannel
|
|
||||||
$auditResult.Details = "AllowEmailIntoChannel is set to $allowEmailIntoChannel"
|
|
||||||
$auditResult.FailureReason = if ($allowEmailIntoChannel) { "Emails can be sent to a channel email address" } else { "N/A" }
|
|
||||||
$auditResult.Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($allowEmailIntoChannel) {
|
||||||
|
"AllowEmailIntoChannel is set to True"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"AllowEmailIntoChannel is set to False"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = -not $allowEmailIntoChannel
|
||||||
|
Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,51 +1,90 @@
|
|||||||
function Test-BlockMailForwarding {
|
function Test-BlockMailForwarding {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResult = [CISAuditResult]::new()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult.Rec = "6.2.1"
|
# Initialization code, if needed
|
||||||
$auditResult.ELevel = "E3"
|
$recnum = "6.2.1"
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "0.0"
|
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.RecDescription = "Ensure all forms of mail forwarding are blocked and/or disabled"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Verify that no rules are forwarding the email to external domains
|
try {
|
||||||
$transportRules = Get-TransportRule | Where-Object { $_.RedirectMessageTo -ne $null }
|
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||||
$forwardingBlocked = $transportRules.Count -eq 0
|
|
||||||
|
|
||||||
$auditResult.Result = $forwardingBlocked
|
# Step 1: Retrieve the transport rules that redirect messages
|
||||||
$auditResult.Details = if ($transportRules.Count -gt 0) {
|
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
||||||
$transportRules | ForEach-Object {
|
$transportForwardingBlocked = $transportRules.Count -eq 0
|
||||||
"$($_.Name) redirects to $($_.RedirectMessageTo)"
|
|
||||||
} -join " | "
|
# Step 2: Check all anti-spam outbound policies
|
||||||
} else {
|
$outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy
|
||||||
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark."
|
$nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' }
|
||||||
|
$nonCompliantSpamPoliciesArray = @($nonCompliantSpamPolicies)
|
||||||
|
$spamForwardingBlocked = $nonCompliantSpamPoliciesArray.Count -eq 0
|
||||||
|
|
||||||
|
# Determine overall compliance
|
||||||
|
$forwardingBlocked = $transportForwardingBlocked -and $spamForwardingBlocked
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = @()
|
||||||
|
$details = @()
|
||||||
|
|
||||||
|
if ($transportRules.Count -gt 0) {
|
||||||
|
$failureReasons += "Mail forwarding rules found: $($transportRules.Name -join ', ')"
|
||||||
|
$details += "Transport Rules Details:`nRule Name|Redirects To"
|
||||||
|
$details += $transportRules | ForEach-Object {
|
||||||
|
"$($_.Name)|$($_.RedirectMessageTo -join ', ')"
|
||||||
|
}
|
||||||
|
$details += "`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($nonCompliantSpamPoliciesArray.Count -gt 0) {
|
||||||
|
$failureReasons += "Outbound spam policies allowing automatic forwarding found."
|
||||||
|
$details += "Outbound Spam Policies Details:`nPolicy|AutoForwardingMode"
|
||||||
|
$details += $nonCompliantSpamPoliciesArray | ForEach-Object {
|
||||||
|
"$($_.Name)|$($_.AutoForwardingMode)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($failureReasons.Count -eq 0) {
|
||||||
|
$failureReasons = "N/A"
|
||||||
|
$details = "Both transport rules and outbound spam policies are configured correctly to block forwarding."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$failureReasons = $failureReasons -join " | "
|
||||||
|
$details = $details -join "`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $forwardingBlocked
|
||||||
|
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
$auditResult.FailureReason = if (-not $forwardingBlocked) {
|
catch {
|
||||||
"Mail forwarding rules found: $($transportRules.Name -join ', ')"
|
Write-Error "An error occurred during the test: $_"
|
||||||
} else {
|
|
||||||
"N/A"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult.Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return the result object
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,47 +1,70 @@
|
|||||||
function Test-BlockSharedMailboxSignIn {
|
function Test-BlockSharedMailboxSignIn {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.2.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
|
|
||||||
# Pass if all shared mailboxes have AccountEnabled set to False.
|
|
||||||
# Fail if any shared mailbox has AccountEnabled set to True.
|
|
||||||
|
|
||||||
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
try {
|
||||||
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
|
||||||
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
|
||||||
$allBlocked = $enabledMailboxes.Count -eq 0
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve shared mailbox details
|
||||||
$auditResult = [CISAuditResult]::new()
|
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
||||||
$auditResult.CISControlVer = "v8"
|
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
||||||
$auditResult.CISControl = "0.0" # Control is explicitly not mapped
|
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
$allBlocked = $enabledMailboxes.Count -eq 0
|
||||||
$auditResult.Rec = "1.2.2"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Control is not mapped, hence IG1 is false
|
|
||||||
$auditResult.IG2 = $false # Control is not mapped, hence IG2 is false
|
|
||||||
$auditResult.IG3 = $false # Control is not mapped, hence IG3 is false
|
|
||||||
$auditResult.RecDescription = "Ensure sign-in to shared mailboxes is blocked"
|
|
||||||
$auditResult.Result = $allBlocked
|
|
||||||
$auditResult.Details = "Enabled Mailboxes: $($enabledMailboxes -join ', ')"
|
|
||||||
$auditResult.FailureReason = if ($allBlocked) { "N/A" } else { "Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')" }
|
|
||||||
$auditResult.Status = if ($allBlocked) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $allBlocked) {
|
||||||
|
"Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($allBlocked) {
|
||||||
|
"All shared mailboxes have sign-in blocked."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Enabled Mailboxes: $($enabledMailboxes -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allBlocked
|
||||||
|
Status = if ($allBlocked) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,67 @@
|
|||||||
function Test-CommonAttachmentFilter {
|
function Test-CommonAttachmentFilter {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResults = @()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
try {
|
||||||
# Pass if EnableFileFilter is set to True. Fail otherwise.
|
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
||||||
|
|
||||||
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
# Retrieve the attachment filter policy
|
||||||
$result = $attachmentFilter.EnableFileFilter
|
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
||||||
$details = "File Filter Enabled: $($attachmentFilter.EnableFileFilter)"
|
$result = $attachmentFilter.EnableFileFilter
|
||||||
$failureReason = if ($result) { "N/A" } else { "Common Attachment Types Filter is disabled" }
|
|
||||||
$status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $result) {
|
||||||
$auditResult.Status = $status
|
"Common Attachment Types Filter is disabled"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "2.1.2"
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure the Common Attachment Types Filter is enabled"
|
}
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "9.6"
|
|
||||||
$auditResult.CISDescription = "Block Unnecessary File Types"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $result
|
|
||||||
$auditResult.Details = $details
|
|
||||||
$auditResult.FailureReason = $failureReason
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($result) {
|
||||||
|
"File Filter Enabled: True"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"File Filter Enabled: False"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +1,68 @@
|
|||||||
function Test-CustomerLockbox {
|
function Test-CustomerLockbox {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here
|
# Aligned
|
||||||
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResults = @()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.3.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
|
||||||
|
try {
|
||||||
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled
|
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled
|
||||||
|
|
||||||
|
# Retrieve the organization configuration
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
||||||
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
|
$failureReasons = if (-not $customerLockboxEnabled) {
|
||||||
$auditResult.ELevel = "E5"
|
"Customer lockbox feature is not enabled."
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.Rec = "1.3.6"
|
else {
|
||||||
$auditResult.RecDescription = "Ensure the customer lockbox feature is enabled"
|
"N/A"
|
||||||
$auditResult.CISControlVer = 'v8'
|
}
|
||||||
$auditResult.CISControl = "0.0" # As per the snapshot provided, this is explicitly not mapped
|
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.Result = $customerLockboxEnabled
|
|
||||||
$auditResult.Details = "Customer Lockbox Enabled: $customerLockboxEnabled"
|
|
||||||
$auditResult.FailureReason = if ($customerLockboxEnabled) { "N/A" } else { "Customer lockbox feature is not enabled." }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($customerLockboxEnabled) {
|
||||||
|
"Customer Lockbox Enabled: True"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Customer Lockbox Enabled: False"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object #
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $customerLockboxEnabled
|
||||||
|
Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,68 @@
|
|||||||
function Test-DialInBypassLobby {
|
function Test-DialInBypassLobby {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
try {
|
||||||
|
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
|
# Retrieve Teams meeting policy for PSTN users
|
||||||
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
|
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
|
||||||
|
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $PSTNBypassDisabled) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Users dialing in can bypass the lobby"
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.5.4"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure users dialing in can't bypass the lobby"
|
|
||||||
$auditResult.Result = $PSTNBypassDisabled
|
|
||||||
$auditResult.Details = "AllowPSTNUsersToBypassLobby is set to $($CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby)"
|
|
||||||
$auditResult.FailureReason = if ($PSTNBypassDisabled) { "N/A" } else { "Users dialing in can bypass the lobby" }
|
|
||||||
$auditResult.Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($PSTNBypassDisabled) {
|
||||||
|
"AllowPSTNUsersToBypassLobby is set to False"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"AllowPSTNUsersToBypassLobby is set to True"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $PSTNBypassDisabled
|
||||||
|
Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,41 +1,69 @@
|
|||||||
function Test-DisallowInfectedFilesDownload {
|
function Test-DisallowInfectedFilesDownload {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here
|
# Aligned
|
||||||
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.3.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
|
|
||||||
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
|
||||||
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
try {
|
||||||
$auditResult.CISControlVer = "v8"
|
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
|
||||||
$auditResult.CISControl = "10.1"
|
|
||||||
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
|
|
||||||
|
|
||||||
$auditResult.Rec = "7.3.1"
|
# Retrieve the SharePoint tenant configuration
|
||||||
$auditResult.ELevel = "E5"
|
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
||||||
$auditResult.ProfileLevel = "L2"
|
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure Office 365 SharePoint infected files are disallowed for download"
|
|
||||||
|
|
||||||
$auditResult.Result = $isDisallowInfectedFileDownloadEnabled
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.Details = "DisallowInfectedFileDownload: $($SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload)"
|
$failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) {
|
||||||
$auditResult.FailureReason = if (-not $isDisallowInfectedFileDownloadEnabled) { "Downloading infected files is not disallowed." } else { "N/A" }
|
"Downloading infected files is not disallowed."
|
||||||
$auditResult.Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($isDisallowInfectedFileDownloadEnabled) {
|
||||||
|
"DisallowInfectedFileDownload: True"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"DisallowInfectedFileDownload: False"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isDisallowInfectedFileDownloadEnabled
|
||||||
|
Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,44 +1,69 @@
|
|||||||
function Test-EnableDKIM {
|
function Test-EnableDKIM {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
|
||||||
|
try {
|
||||||
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
|
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
|
||||||
# Pass if Enabled is True for all domains. Fail if any domain has Enabled set to False.
|
|
||||||
|
# Retrieve DKIM configuration for all domains
|
||||||
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
|
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
|
||||||
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
|
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
|
||||||
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
|
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $dkimResult) {
|
||||||
$auditResult.Status = if ($dkimResult) { "Pass" } else { "Fail" }
|
"DKIM is not enabled for some domains"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "2.1.9"
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure that DKIM is enabled for all Exchange Online Domains"
|
}
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "9.5"
|
|
||||||
$auditResult.CISDescription = "Implement DMARC"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $dkimResult
|
|
||||||
$auditResult.Details = if (-not $dkimResult) { "DKIM not enabled for: $($dkimFailedDomains -join ', ')" } else { "All domains have DKIM enabled" }
|
|
||||||
$auditResult.FailureReason = if (-not $dkimResult) { "DKIM is not enabled for some domains" } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($dkimResult) {
|
||||||
|
"All domains have DKIM enabled"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"DKIM not enabled for: $($dkimFailedDomains -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $dkimResult
|
||||||
|
Status = if ($dkimResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,69 @@
|
|||||||
function Test-ExternalNoControl {
|
function Test-ExternalNoControl {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
|
||||||
|
try {
|
||||||
# 8.5.7 (L1) Ensure external participants can't give or request control
|
# 8.5.7 (L1) Ensure external participants can't give or request control
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Retrieve Teams meeting policy for external participant control
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
|
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
|
||||||
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
|
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $externalControlRestricted) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"External participants can give or request control"
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.5.7"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure external participants can't give or request control"
|
|
||||||
$auditResult.Result = $externalControlRestricted
|
|
||||||
$auditResult.Details = "AllowExternalParticipantGiveRequestControl is set to $($CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl)"
|
|
||||||
$auditResult.FailureReason = if ($externalControlRestricted) { "N/A" } else { "External participants can give or request control" }
|
|
||||||
$auditResult.Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($externalControlRestricted) {
|
||||||
|
"AllowExternalParticipantGiveRequestControl is set to False"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"AllowExternalParticipantGiveRequestControl is set to True"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $externalControlRestricted
|
||||||
|
Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,53 +1,78 @@
|
|||||||
function Test-ExternalSharingCalendars {
|
function Test-ExternalSharingCalendars {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.3.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
|
|
||||||
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
|
||||||
|
|
||||||
# Check if calendar sharing is disabled in all applicable policies
|
try {
|
||||||
$isExternalSharingDisabled = $true
|
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
|
||||||
$sharingPolicyDetails = @()
|
|
||||||
foreach ($policy in $sharingPolicies) {
|
# Retrieve sharing policies related to calendar sharing
|
||||||
if ($policy.Enabled -eq $true) {
|
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
||||||
$isExternalSharingDisabled = $false
|
|
||||||
$sharingPolicyDetails += "$($policy.Name): Enabled"
|
# Check if calendar sharing is disabled in all applicable policies
|
||||||
|
$isExternalSharingDisabled = $true
|
||||||
|
$sharingPolicyDetails = @()
|
||||||
|
foreach ($policy in $sharingPolicies) {
|
||||||
|
if ($policy.Enabled -eq $true) {
|
||||||
|
$isExternalSharingDisabled = $false
|
||||||
|
$sharingPolicyDetails += "$($policy.Name): Enabled"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $isExternalSharingDisabled) {
|
||||||
|
"Calendar sharing with external users is enabled in one or more policies."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($isExternalSharingDisabled) {
|
||||||
|
"Calendar sharing with external users is disabled."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isExternalSharingDisabled
|
||||||
|
Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve the description from the test definitions
|
||||||
$auditResult = [CISAuditResult]::new()
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.Rec = "1.3.3"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.RecDescription = "Ensure 'External sharing' of calendars is not available"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
# The following IG values are placeholders. Replace with actual values when known.
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
# Placeholder for CIS Control, to be replaced with the actual value when available
|
|
||||||
$auditResult.CISControl = "4.8"
|
|
||||||
$auditResult.CISDescription = "Uninstall or Disable Unnecessary Services on Enterprise Assets and Software"
|
|
||||||
$auditResult.Result = $isExternalSharingDisabled
|
|
||||||
$auditResult.Details = if ($isExternalSharingDisabled) { "Calendar sharing with external users is disabled." } else { "Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')" }
|
|
||||||
$auditResult.FailureReason = if ($isExternalSharingDisabled) { "N/A" } else { "Calendar sharing with external users is enabled in one or more policies." }
|
|
||||||
$auditResult.Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,70 @@
|
|||||||
function Test-GlobalAdminsCount {
|
function Test-GlobalAdminsCount {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here
|
# Aligned
|
||||||
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.1.3 (L1) Ensure that between two and four global admins are designated
|
try {
|
||||||
# Pass if the count of global admins is between 2 and 4. Fail otherwise.
|
# 1.1.3 (L1) Ensure that between two and four global admins are designated
|
||||||
|
|
||||||
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
|
# Retrieve global admin role and members
|
||||||
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
|
||||||
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
|
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
||||||
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
|
$globalAdminCount = $globalAdmins.Count
|
||||||
|
$globalAdminUsernames = ($globalAdmins | ForEach-Object {
|
||||||
|
"$($_.AdditionalProperties["displayName"]) ($($_.AdditionalProperties["userPrincipalName"]))"
|
||||||
|
}) -join ', '
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($globalAdminCount -lt 2) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Less than 2 global admins: $globalAdminUsernames"
|
||||||
$auditResult.CISControl = "5.1"
|
}
|
||||||
$auditResult.CISDescription = "Establish and Maintain an Inventory of Accounts"
|
elseif ($globalAdminCount -gt 4) {
|
||||||
$auditResult.Rec = "1.1.3"
|
"More than 4 global admins: $globalAdminUsernames"
|
||||||
$auditResult.ELevel = "E3" # Based on your environment (E3, E5, etc.)
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.IG1 = $true # Set based on the benchmark
|
"N/A"
|
||||||
$auditResult.IG2 = $true # Set based on the benchmark
|
}
|
||||||
$auditResult.IG3 = $true # Set based on the benchmark
|
|
||||||
$auditResult.RecDescription = "Ensure that between two and four global admins are designated"
|
|
||||||
$auditResult.Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
|
|
||||||
$auditResult.Details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
|
|
||||||
$auditResult.FailureReason = if ($globalAdminCount -lt 2) { "Less than 2 global admins: $globalAdminUsernames" } elseif ($globalAdminCount -gt 4) { "More than 4 global admins: $globalAdminUsernames" } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
|
||||||
|
Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,41 +1,64 @@
|
|||||||
function Test-GuestAccessExpiration {
|
function Test-GuestAccessExpiration {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here
|
# Aligned
|
||||||
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
|
||||||
|
try {
|
||||||
# 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
|
# 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
|
||||||
|
|
||||||
|
# Retrieve SharePoint tenant settings related to guest access expiration
|
||||||
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
|
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
|
||||||
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
|
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isGuestAccessExpirationConfiguredCorrectly) {
|
||||||
$auditResult.CISControl = "0.0"
|
"Guest access expiration is not configured to automatically expire within 30 days or less."
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.9"
|
$details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.RecDescription = "Ensure guest access to a site or OneDrive will expire automatically"
|
|
||||||
|
|
||||||
$auditResult.Result = $isGuestAccessExpirationConfiguredCorrectly
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
|
$params = @{
|
||||||
$auditResult.FailureReason = if (-not $isGuestAccessExpirationConfiguredCorrectly) { "Guest access expiration is not configured to automatically expire within 30 days or less." } else { "N/A" }
|
Rec = $recnum
|
||||||
$auditResult.Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
Result = $isGuestAccessExpirationConfiguredCorrectly
|
||||||
|
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
70
source/tests/Test-GuestUsersBiweeklyReview.ps1
Normal file
70
source/tests/Test-GuestUsersBiweeklyReview.ps1
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
function Test-GuestUsersBiweeklyReview {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
|
param (
|
||||||
|
# Aligned
|
||||||
|
# Define your parameters here if needed
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.1.4"
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
try {
|
||||||
|
# 1.1.4 (L1) Ensure Guest Users are reviewed at least biweekly
|
||||||
|
|
||||||
|
|
||||||
|
# Retrieve guest users from Microsoft Graph
|
||||||
|
# Connect-MgGraph -Scopes "User.Read.All"
|
||||||
|
$guestUsers = Get-MgUser -All -Filter "UserType eq 'Guest'"
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if ($guestUsers) {
|
||||||
|
"Guest users present: $($guestUsers.Count)"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($guestUsers) {
|
||||||
|
$auditCommand = "Get-MgUser -All -Property UserType,UserPrincipalName | Where {`$_.UserType -ne 'Member'} | Format-Table UserPrincipalName, UserType"
|
||||||
|
"Manual review required. To list guest users, run: `"$auditCommand`"."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"No guest users found."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = -not $guestUsers
|
||||||
|
Status = if ($guestUsers) { "Fail" } else { "Pass" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end {
|
||||||
|
# Return the audit result
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
@@ -1,44 +1,64 @@
|
|||||||
function Test-IdentifyExternalEmail {
|
function Test-IdentifyExternalEmail {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.2.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.3 (L1) Ensure email from external senders is identified
|
|
||||||
# Requirement is to have external sender tagging enabled
|
|
||||||
|
|
||||||
$externalInOutlook = Get-ExternalInOutlook
|
try {
|
||||||
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
# 6.2.3 (L1) Ensure email from external senders is identified
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve external sender tagging configuration
|
||||||
$auditResult = [CISAuditResult]::new()
|
$externalInOutlook = Get-ExternalInOutlook
|
||||||
$auditResult.Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "6.2.3"
|
|
||||||
$auditResult.RecDescription = "Ensure email from external senders is identified"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "0.0"
|
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.Result = $externalTaggingEnabled
|
|
||||||
$auditResult.Details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
|
|
||||||
$auditResult.FailureReason = if (-not $externalTaggingEnabled) { "External sender tagging is disabled" } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $externalTaggingEnabled) {
|
||||||
|
"External sender tagging is disabled"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $externalTaggingEnabled
|
||||||
|
Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,64 @@
|
|||||||
function Test-LinkSharingRestrictions {
|
function Test-LinkSharingRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
# Test behavior in prod
|
# Test behavior in prod
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
|
try {
|
||||||
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
|
||||||
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Retrieve link sharing configuration for SharePoint and OneDrive
|
||||||
$auditResult.CISControlVer = "v8"
|
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
||||||
$auditResult.CISControl = "3.3"
|
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.7"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if (-not $isLinkSharingRestricted) {
|
||||||
$auditResult.ProfileLevel = "L1"
|
"Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||||
$auditResult.IG1 = $true
|
}
|
||||||
$auditResult.IG2 = $true
|
else {
|
||||||
$auditResult.IG3 = $true
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure link sharing is restricted in SharePoint and OneDrive"
|
}
|
||||||
|
|
||||||
$auditResult.Result = $isLinkSharingRestricted
|
$details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||||
$auditResult.Details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
|
||||||
$auditResult.FailureReason = if (-not $isLinkSharingRestricted) { "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)" } else { "N/A" }
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isLinkSharingRestricted
|
||||||
|
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,70 @@
|
|||||||
function Test-MailTipsEnabled {
|
function Test-MailTipsEnabled {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here
|
# Aligned
|
||||||
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "6.5.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
try {
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
||||||
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
|
||||||
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
|
||||||
|
|
||||||
# Since there is no direct CIS Control mapping, the control will be set as not applicable.
|
# Retrieve organization configuration for MailTips settings
|
||||||
$auditResult.CISControl = "0"
|
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
||||||
$auditResult.CISControlVer = "v8"
|
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
||||||
|
|
||||||
$auditResult.Rec = "6.5.2"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if (-not ($allTipsEnabled -and $externalRecipientsTipsEnabled)) {
|
||||||
$auditResult.ProfileLevel = "L2"
|
"One or more MailTips settings are not configured as required."
|
||||||
$auditResult.IG1 = $false
|
}
|
||||||
$auditResult.IG2 = $false
|
else {
|
||||||
$auditResult.IG3 = $false
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure MailTips are enabled for end users"
|
}
|
||||||
|
|
||||||
$auditResult.Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
$details = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) {
|
||||||
$auditResult.Details = "MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
"MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
||||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "One or more MailTips settings are not configured as required." } else { "N/A" }
|
}
|
||||||
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
|
else {
|
||||||
|
"One or more MailTips settings are not configured as required."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
||||||
|
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,39 +1,33 @@
|
|||||||
function Test-MailboxAuditingE3 {
|
function Test-MailboxAuditingE3 {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION")
|
$e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION")
|
||||||
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "6.1.2"
|
|
||||||
$auditResult.RecDescription = "Ensure mailbox auditing for Office E3 users is Enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "8.2"
|
|
||||||
$auditResult.CISDescription = "Collect audit logs."
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
|
|
||||||
$allFailures = @()
|
$allFailures = @()
|
||||||
$allUsers = Get-AzureADUser -All $true
|
$allUsers = Get-AzureADUser -All $true
|
||||||
$processedUsers = @{} # Dictionary to track processed users
|
$processedUsers = @{} # Dictionary to track processed users
|
||||||
|
$recnum = "6.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
foreach ($user in $allUsers) {
|
try {
|
||||||
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
foreach ($user in $allUsers) {
|
||||||
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
||||||
continue
|
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
||||||
}
|
continue
|
||||||
try {
|
}
|
||||||
|
|
||||||
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
||||||
$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
|
$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
|
||||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
|
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
|
||||||
@@ -53,59 +47,63 @@ function Test-MailboxAuditingE3 {
|
|||||||
foreach ($action in $OwnerActions) {
|
foreach ($action in $OwnerActions) {
|
||||||
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($missingActions.Count -gt 0) {
|
||||||
|
$formattedActions = Format-MissingAction -missingActions $missingActions
|
||||||
|
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$allFailures += "$userUPN`: AuditEnabled - False"
|
$allFailures += "$userUPN|False|||"
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
|
||||||
}
|
|
||||||
# Mark the user as processed
|
# Mark the user as processed
|
||||||
$processedUsers[$user.UserPrincipalName] = $true
|
$processedUsers[$user.UserPrincipalName] = $true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
|
||||||
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$auditResult.Result = $allFailures.Count -eq 0
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
|
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
||||||
$auditResult.Details = if ($auditResult.Result) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
$details = if ($allFailures.Count -eq 0) {
|
||||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
|
"All Office E3 users have correct mailbox audit settings."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
#$verbosePreference = 'Continue'
|
||||||
|
$detailsLength = $details.Length
|
||||||
|
Write-Verbose "Character count of the details: $detailsLength"
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
|
||||||
param ([array]$missingActions)
|
|
||||||
|
|
||||||
$actionGroups = @{
|
|
||||||
"Admin" = @()
|
|
||||||
"Delegate" = @()
|
|
||||||
"Owner" = @()
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($action in $missingActions) {
|
|
||||||
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
|
|
||||||
$type = $matches[1]
|
|
||||||
$actionName = $matches[2]
|
|
||||||
$actionGroups[$type] += $actionName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$formattedResults = @()
|
|
||||||
foreach ($type in $actionGroups.Keys) {
|
|
||||||
if ($actionGroups[$type].Count -gt 0) {
|
|
||||||
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formattedResults -join '; '
|
|
||||||
}
|
|
||||||
|
@@ -1,45 +1,39 @@
|
|||||||
function Test-MailboxAuditingE5 {
|
function Test-MailboxAuditingE5 {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param ()
|
[OutputType([CISAuditResult])]
|
||||||
|
param (
|
||||||
|
# Parameters can be added if needed
|
||||||
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
|
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
|
||||||
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.ELevel = "E5"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "6.1.3"
|
|
||||||
$auditResult.RecDescription = "Ensure mailbox auditing for Office E5 users is Enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "8.2"
|
|
||||||
$auditResult.CISDescription = "Collect audit logs."
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
|
|
||||||
$allFailures = @()
|
$allFailures = @()
|
||||||
$allUsers = Get-AzureADUser -All $true
|
$allUsers = Get-AzureADUser -All $true
|
||||||
$processedUsers = @{} # Dictionary to track processed users
|
$processedUsers = @{} # Dictionary to track processed users
|
||||||
|
$recnum = "6.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
foreach ($user in $allUsers) {
|
try {
|
||||||
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
foreach ($user in $allUsers) {
|
||||||
continue
|
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
||||||
}
|
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
# Define SKU Part Numbers for Office E5 licenses
|
|
||||||
# Define SKU Part Numbers for Office E5 licenses
|
|
||||||
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
|
|
||||||
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
||||||
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
||||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
||||||
|
|
||||||
if ($hasOfficeE5) {
|
if ($hasOfficeE5) {
|
||||||
$userUPN = $user.UserPrincipalName
|
$userUPN = $user.UserPrincipalName
|
||||||
|
|
||||||
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
||||||
|
|
||||||
$missingActions = @()
|
$missingActions = @()
|
||||||
@@ -53,68 +47,63 @@ function Test-MailboxAuditingE5 {
|
|||||||
foreach ($action in $OwnerActions) {
|
foreach ($action in $OwnerActions) {
|
||||||
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($missingActions.Count -gt 0) {
|
||||||
|
$formattedActions = Format-MissingAction -missingActions $missingActions
|
||||||
|
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$allFailures += "$userUPN`: AuditEnabled - False"
|
$allFailures += "$userUPN|False|||"
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
# Mark the user as processed
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Write-Verbose "User $($user.UserPrincipalName) passed the mailbox audit checks."
|
|
||||||
}
|
|
||||||
$processedUsers[$user.UserPrincipalName] = $true
|
$processedUsers[$user.UserPrincipalName] = $true
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
# Adding verbose output to indicate the user does not have an E5 license
|
|
||||||
Write-Verbose "User $($user.UserPrincipalName) does not have an Office E5 license."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch {
|
|
||||||
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($allFailures.Count -eq 0) {
|
# Prepare failure reasons and details based on compliance
|
||||||
Write-Verbose "All evaluated E5 users have correct mailbox audit settings."
|
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
||||||
|
$details = if ($allFailures.Count -eq 0) {
|
||||||
|
"All Office E5 users have correct mailbox audit settings."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult.Result = $allFailures.Count -eq 0
|
|
||||||
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.Details = if ($auditResult.Result) { "All Office E5 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
|
||||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
#$verbosePreference = 'Continue'
|
||||||
|
$detailsLength = $details.Length
|
||||||
|
Write-Verbose "Character count of the details: $detailsLength"
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
|
||||||
param ([array]$missingActions)
|
|
||||||
|
|
||||||
$actionGroups = @{
|
|
||||||
"Admin" = @()
|
|
||||||
"Delegate" = @()
|
|
||||||
"Owner" = @()
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($action in $missingActions) {
|
|
||||||
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
|
|
||||||
$type = $matches[1]
|
|
||||||
$actionName = $matches[2]
|
|
||||||
$actionGroups[$type] += $actionName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$formattedResults = @()
|
|
||||||
foreach ($type in $actionGroups.Keys) {
|
|
||||||
if ($actionGroups[$type].Count -gt 0) {
|
|
||||||
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formattedResults -join '; '
|
|
||||||
}
|
|
@@ -1,54 +1,67 @@
|
|||||||
function Test-ManagedApprovedPublicGroups {
|
function Test-ManagedApprovedPublicGroups {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here
|
# Aligned
|
||||||
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
|
try {
|
||||||
|
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
|
||||||
|
|
||||||
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
# Retrieve all public groups
|
||||||
|
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
||||||
|
|
||||||
# Check if there are public groups and if they are organizationally managed/approved
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"There are public groups present that are not organizationally managed/approved."
|
||||||
$auditResult.CISControl = "3.3"
|
}
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
else {
|
||||||
$auditResult.Rec = "1.2.1"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true # Based on the provided CIS Control image, IG3 is not applicable
|
|
||||||
$auditResult.RecDescription = "Ensure that only organizationally managed/approved public groups exist"
|
|
||||||
|
|
||||||
if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
|
$details = if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
|
||||||
$auditResult.Result = $true
|
"No public groups found."
|
||||||
$auditResult.Details = "No public groups found."
|
}
|
||||||
$auditResult.FailureReason = "N/A"
|
else {
|
||||||
$auditResult.Status = "Pass"
|
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
||||||
|
"Public groups found: $($groupDetails -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $null -eq $allGroups -or $allGroups.Count -eq 0
|
||||||
|
Status = if ($null -eq $allGroups -or $allGroups.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
else {
|
catch {
|
||||||
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
Write-Error "An error occurred during the test: $_"
|
||||||
$detailsString = $groupDetails -join ', '
|
|
||||||
|
|
||||||
$auditResult.Result = $false
|
# Retrieve the description from the test definitions
|
||||||
$auditResult.Details = "Public groups found: $detailsString"
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.FailureReason = "There are public groups present that are not organizationally managed/approved."
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.Status = "Fail"
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
$auditResults += $auditResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResults
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,64 @@
|
|||||||
function Test-MeetingChatNoAnonymous {
|
function Test-MeetingChatNoAnonymous {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
try {
|
||||||
# Name doesn't match profile level in benchmarks either.
|
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
# Retrieve the Teams meeting policy for meeting chat
|
||||||
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
|
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
||||||
|
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($chatAnonDisabled) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"N/A"
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.5.5"
|
"Meeting chat allows anonymous users"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure meeting chat does not allow anonymous users"
|
|
||||||
$auditResult.Result = $chatAnonDisabled
|
|
||||||
$auditResult.Details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
|
|
||||||
$auditResult.FailureReason = if ($chatAnonDisabled) { "N/A" } else { "Meeting chat allows anonymous users" }
|
|
||||||
$auditResult.Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $chatAnonDisabled
|
||||||
|
Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,54 +1,63 @@
|
|||||||
function Test-ModernAuthExchangeOnline {
|
function Test-ModernAuthExchangeOnline {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResults = [CISAuditResult]::new()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.5.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
try {
|
||||||
# Ensuring the ExchangeOnlineManagement module is available
|
# Ensuring the ExchangeOnlineManagement module is available
|
||||||
|
|
||||||
|
|
||||||
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
|
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $orgConfig.OAuth2ClientProfileEnabled) {
|
||||||
|
"Modern authentication is disabled"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResults.CISControlVer = "v8"
|
$details = "OAuth2ClientProfileEnabled: $($orgConfig.OAuth2ClientProfileEnabled) for Organization: $($orgConfig.Name)"
|
||||||
$auditResults.CISControl = "3.10"
|
|
||||||
$auditResults.CISDescription = "Encrypt Sensitive Data in Transit"
|
|
||||||
$auditResults.IG1 = $false # As per CIS Control v8 mapping for IG1
|
|
||||||
$auditResults.IG2 = $true # As per CIS Control v8 mapping for IG2
|
|
||||||
$auditResults.IG3 = $true # As per CIS Control v8 mapping for IG3
|
|
||||||
$auditResults.ELevel = "E3" # Based on your environment (E3, E5, etc.)
|
|
||||||
$auditResults.Profile = "L1"
|
|
||||||
$auditResults.Rec = "6.5.1"
|
|
||||||
$auditResults.RecDescription = "Ensure modern authentication for Exchange Online is enabled (Automated)"
|
|
||||||
$auditResults.Result = $orgConfig.OAuth2ClientProfileEnabled
|
|
||||||
$auditResults.Details = $auditResults.Details = $orgConfig.Name + " OAuth2ClientProfileEnabled: " + $orgConfig.OAuth2ClientProfileEnabled
|
|
||||||
$auditResults.FailureReason = if (-not $orgConfig.OAuth2ClientProfileEnabled) { "Modern authentication is disabled" } else { "N/A" }
|
|
||||||
$auditResults.Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $orgConfig.OAuth2ClientProfileEnabled
|
||||||
|
Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Error "An error occurred while testing modern authentication for Exchange Online: $_"
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,39 +1,60 @@
|
|||||||
function Test-ModernAuthSharePoint {
|
function Test-ModernAuthSharePoint {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
|
||||||
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
|
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
||||||
|
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $modernAuthForSPRequired) {
|
||||||
$auditResult.CISControl = "3.10"
|
"Legacy authentication protocols are enabled"
|
||||||
$auditResult.CISDescription = "Encrypt Sensitive Data in Transit"
|
}
|
||||||
$auditResult.Rec = "7.2.1"
|
else {
|
||||||
$auditResult.ELevel = "E3"
|
"N/A"
|
||||||
$auditResult.ProfileLevel = "L1"
|
}
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
$details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)"
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Modern Authentication for SharePoint Applications"
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Result = $modernAuthForSPRequired
|
$params = @{
|
||||||
$auditResult.Details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)"
|
Rec = $recnum
|
||||||
$auditResult.FailureReason = if (-not $modernAuthForSPRequired) { "Legacy authentication protocols are enabled" } else { "N/A" }
|
Result = $modernAuthForSPRequired
|
||||||
$auditResult.Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
|
Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,63 @@
|
|||||||
function Test-NoAnonymousMeetingJoin {
|
function Test-NoAnonymousMeetingJoin {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
|
try {
|
||||||
|
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
|
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
|
||||||
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
|
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($allowAnonymousUsersToJoinMeeting) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Anonymous users are allowed to join meetings"
|
||||||
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.5.1"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure anonymous users can't join a meeting"
|
|
||||||
$auditResult.Result = -not $allowAnonymousUsersToJoinMeeting
|
|
||||||
$auditResult.Details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
|
|
||||||
$auditResult.FailureReason = if ($allowAnonymousUsersToJoinMeeting) { "Anonymous users are allowed to join meetings" } else { "N/A" }
|
|
||||||
$auditResult.Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = -not $allowAnonymousUsersToJoinMeeting
|
||||||
|
Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,63 @@
|
|||||||
function Test-NoAnonymousMeetingStart {
|
function Test-NoAnonymousMeetingStart {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
|
try {
|
||||||
|
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
|
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
|
||||||
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
|
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($anonymousStartDisabled) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"N/A"
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.5.2"
|
"Anonymous users and dial-in callers can start a meeting"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure anonymous users and dial-in callers can't start a meeting"
|
|
||||||
$auditResult.Result = $anonymousStartDisabled
|
|
||||||
$auditResult.Details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)"
|
|
||||||
$auditResult.FailureReason = if ($anonymousStartDisabled) { "N/A" } else { "Anonymous users and dial-in callers can start a meeting" }
|
|
||||||
$auditResult.Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $anonymousStartDisabled
|
||||||
|
Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,62 @@
|
|||||||
function Test-NoWhitelistDomains {
|
function Test-NoWhitelistDomains {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.2.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
try {
|
||||||
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $_.SenderDomainIs -ne $null }
|
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
||||||
|
|
||||||
$auditResult.CISControl = "0.0"
|
# Retrieve transport rules that whitelist specific domains
|
||||||
$auditResult.CISControlVer = "v8"
|
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
|
|
||||||
$auditResult.Rec = "6.2.2"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if ($whitelistedRules) {
|
||||||
$auditResult.ProfileLevel = "L1"
|
"There are transport rules whitelisting specific domains."
|
||||||
$auditResult.IG1 = $false
|
}
|
||||||
$auditResult.IG2 = $false
|
else {
|
||||||
$auditResult.IG3 = $false
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure mail transport rules do not whitelist specific domains"
|
}
|
||||||
|
|
||||||
if ($whitelistedRules) {
|
$details = if ($whitelistedRules) {
|
||||||
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
|
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
|
||||||
$auditResult.Result = $false
|
"Whitelisted Rules: $($ruleDetails -join '; ')"
|
||||||
$auditResult.Details = "Whitelisted Rules: $($ruleDetails -join '; ')"
|
}
|
||||||
$auditResult.FailureReason = "There are transport rules whitelisting specific domains."
|
else {
|
||||||
$auditResult.Status = "Fail"
|
"No transport rules whitelisting specific domains found."
|
||||||
} else {
|
}
|
||||||
$auditResult.Result = $true
|
|
||||||
$auditResult.Details = "No transport rules whitelisting specific domains found."
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.FailureReason = "N/A"
|
$params = @{
|
||||||
$auditResult.Status = "Pass"
|
Rec = $recnum
|
||||||
|
Result = -not $whitelistedRules
|
||||||
|
Status = if ($whitelistedRules) { "Fail" } else { "Pass" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,52 +1,76 @@
|
|||||||
function Test-NotifyMalwareInternal {
|
function Test-NotifyMalwareInternal {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResults = @()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Retrieve all 'Custom' malware filter policies and check notification settings
|
try {
|
||||||
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
||||||
$policiesToReport = @()
|
|
||||||
|
|
||||||
foreach ($policy in $malwareNotifications) {
|
# Retrieve all 'Custom' malware filter policies and check notification settings
|
||||||
if ($policy.EnableInternalSenderAdminNotifications -ne $true) {
|
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
||||||
$policiesToReport += "$($policy.Identity): Notifications Disabled"
|
$policiesToReport = @()
|
||||||
|
|
||||||
|
foreach ($policy in $malwareNotifications) {
|
||||||
|
if ($policy.EnableInternalSenderAdminNotifications -ne $true) {
|
||||||
|
$policiesToReport += "$($policy.Identity): Notifications Disabled"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Determine the result based on the presence of custom policies without notifications
|
||||||
|
$result = $policiesToReport.Count -eq 0
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if ($result) {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Some custom policies do not have notifications for internal users sending malware enabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($result) {
|
||||||
|
"All custom malware policies have notifications enabled."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Misconfigured Policies: $($policiesToReport -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Determine the result based on the presence of custom policies without notifications
|
# Retrieve the description from the test definitions
|
||||||
$result = $policiesToReport.Count -eq 0
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$details = if ($result) { "All custom malware policies have notifications enabled." } else { "Misconfigured Policies: $($policiesToReport -join ', ')" }
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$failureReason = if ($result) { "N/A" } else { "Some custom policies do not have notifications for internal users sending malware enabled." }
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "2.1.3"
|
|
||||||
$auditResult.RecDescription = "Ensure notifications for internal users sending malware is Enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "17.5"
|
|
||||||
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $result
|
|
||||||
$auditResult.Details = $details
|
|
||||||
$auditResult.FailureReason = $failureReason
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,63 @@
|
|||||||
function Test-OneDriveContentRestrictions {
|
function Test-OneDriveContentRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||||
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Retrieve OneDrive sharing capability settings
|
||||||
$auditResult.CISControlVer = "v8"
|
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
||||||
$auditResult.CISControl = "3.3"
|
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.4"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if (-not $isOneDriveSharingRestricted) {
|
||||||
$auditResult.ProfileLevel = "L2"
|
"OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)"
|
||||||
$auditResult.IG1 = $true
|
}
|
||||||
$auditResult.IG2 = $true
|
else {
|
||||||
$auditResult.IG3 = $true
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure OneDrive content sharing is restricted"
|
}
|
||||||
|
|
||||||
$auditResult.Result = $isOneDriveSharingRestricted
|
$details = if ($isOneDriveSharingRestricted) {
|
||||||
$auditResult.Details = "OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
"OneDrive content sharing is restricted."
|
||||||
$auditResult.FailureReason = if (-not $isOneDriveSharingRestricted) { "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)" } else { "N/A" }
|
}
|
||||||
$auditResult.Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
else {
|
||||||
|
"OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isOneDriveSharingRestricted
|
||||||
|
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,37 +1,63 @@
|
|||||||
function Test-OneDriveSyncRestrictions {
|
function Test-OneDriveSyncRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.3.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
|
try {
|
||||||
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
|
||||||
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Retrieve OneDrive sync client restriction settings
|
||||||
$auditResult.CISControlVer = "v8"
|
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
||||||
$auditResult.CISControl = "0.0"
|
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
|
|
||||||
$auditResult.Rec = "7.3.2"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if (-not $isSyncRestricted) {
|
||||||
$auditResult.ProfileLevel = "L2"
|
"OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs."
|
||||||
$auditResult.IG1 = $false
|
}
|
||||||
$auditResult.IG2 = $false
|
else {
|
||||||
$auditResult.IG3 = $false
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure OneDrive sync is restricted for unmanaged devices"
|
}
|
||||||
|
|
||||||
$auditResult.Result = $isSyncRestricted
|
$details = if ($isSyncRestricted) {
|
||||||
$auditResult.Details = "TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
"OneDrive sync is restricted for unmanaged devices."
|
||||||
$auditResult.FailureReason = if (-not $isSyncRestricted) { "OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs." } else { "N/A" }
|
}
|
||||||
$auditResult.Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
else {
|
||||||
|
"TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isSyncRestricted
|
||||||
|
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,45 +1,69 @@
|
|||||||
function Test-OrgOnlyBypassLobby {
|
function Test-OrgOnlyBypassLobby {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
|
try {
|
||||||
|
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
|
# Retrieve the Teams meeting policy for lobby bypass settings
|
||||||
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
|
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
|
||||||
|
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $lobbyBypassRestricted) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"External participants can bypass the lobby"
|
||||||
$auditResult.CISControl = "6.8"
|
}
|
||||||
$auditResult.CISDescription = "Define and Maintain Role-Based Access Control"
|
else {
|
||||||
$auditResult.Rec = "8.5.3"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $true # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure only people in my org can bypass the lobby"
|
|
||||||
$auditResult.Result = $lobbyBypassRestricted
|
|
||||||
$auditResult.Details = "AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
|
|
||||||
$auditResult.FailureReason = if ($lobbyBypassRestricted) { "N/A" } else { "External participants can bypass the lobby" }
|
|
||||||
$auditResult.Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($lobbyBypassRestricted) {
|
||||||
|
"Only people in the organization can bypass the lobby."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $lobbyBypassRestricted
|
||||||
|
Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,69 @@
|
|||||||
function Test-OrganizersPresent {
|
function Test-OrganizersPresent {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
|
try {
|
||||||
|
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
|
# Retrieve the Teams meeting policy for presenters
|
||||||
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
|
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
|
||||||
|
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $presenterRoleRestricted) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Others besides organizers and co-organizers can present"
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.5.6"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure only organizers and co-organizers can present"
|
|
||||||
$auditResult.Result = $presenterRoleRestricted
|
|
||||||
$auditResult.Details = "DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
|
|
||||||
$auditResult.FailureReason = if ($presenterRoleRestricted) { "N/A" } else { "Others besides organizers and co-organizers can present" }
|
|
||||||
$auditResult.Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$details = if ($presenterRoleRestricted) {
|
||||||
|
"Only organizers and co-organizers can present."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $presenterRoleRestricted
|
||||||
|
Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,63 @@
|
|||||||
function Test-PasswordHashSync {
|
function Test-PasswordHashSync {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "5.1.8.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
try {
|
||||||
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
|
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
||||||
$passwordHashSync = Get-MgOrganization | Select-Object OnPremisesSyncEnabled
|
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
|
||||||
$hashSyncResult = $passwordHashSync.OnPremisesSyncEnabled
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve password hash sync status
|
||||||
$auditResult = [CISAuditResult]::new()
|
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
|
||||||
$auditResult.Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
|
$hashSyncResult = $passwordHashSync
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "5.1.8.1"
|
|
||||||
$auditResult.RecDescription = "Ensure password hash sync is enabled for hybrid deployments"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "6.7"
|
|
||||||
$auditResult.CISDescription = "Centralize Access Control"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $hashSyncResult
|
|
||||||
$auditResult.Details = "OnPremisesSyncEnabled: $($passwordHashSync.OnPremisesSyncEnabled)"
|
|
||||||
$auditResult.FailureReason = if (-not $hashSyncResult) { "Password hash sync for hybrid deployments is not enabled" } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $hashSyncResult) {
|
||||||
|
"Password hash sync for hybrid deployments is not enabled"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "OnPremisesSyncEnabled: $($passwordHashSync)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $hashSyncResult
|
||||||
|
Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,87 @@
|
|||||||
function Test-PasswordNeverExpirePolicy {
|
function Test-PasswordNeverExpirePolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory = $false)]
|
||||||
[string]$DomainName # DomainName parameter is now mandatory
|
[string]$DomainName
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.3.1"
|
||||||
|
$overallResult = $true
|
||||||
|
$detailsList = @()
|
||||||
|
$failureReasonsList = @()
|
||||||
|
|
||||||
$auditResults = @()
|
# Add headers for the details
|
||||||
|
$detailsList += "Domain|Validity Period|IsDefault"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
|
try {
|
||||||
# Pass if PasswordValidityPeriodInDays is 0.
|
# Retrieve all domains or a specific domain
|
||||||
# Fail otherwise.
|
$domains = if ($DomainName) {
|
||||||
|
Get-MgDomain -DomainId $DomainName
|
||||||
|
} else {
|
||||||
|
Get-MgDomain
|
||||||
|
}
|
||||||
|
|
||||||
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object PasswordValidityPeriodInDays
|
foreach ($domain in $domains) {
|
||||||
|
$domainName = $domain.Id
|
||||||
|
$isDefault = $domain.IsDefault
|
||||||
|
# Retrieve password expiration policy
|
||||||
|
$passwordPolicy = $domain.PasswordValidityPeriodInDays
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Determine if the policy is compliant
|
||||||
$auditResult = [CISAuditResult]::new()
|
$isCompliant = $passwordPolicy -eq 0
|
||||||
$auditResult.Rec = "1.3.1"
|
$overallResult = $overallResult -and $isCompliant
|
||||||
$auditResult.RecDescription = "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "5.2"
|
|
||||||
$auditResult.CISDescription = "Use Unique Passwords"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true # All are true
|
|
||||||
$auditResult.Result = $passwordPolicy.PasswordValidityPeriodInDays -eq 0
|
|
||||||
$auditResult.Details = "Validity Period: $($passwordPolicy.PasswordValidityPeriodInDays) days"
|
|
||||||
$auditResult.FailureReason = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "N/A" } else { "Password expiration is not set to never expire" }
|
|
||||||
$auditResult.Status = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if ($isCompliant) {
|
||||||
|
"N/A"
|
||||||
|
} else {
|
||||||
|
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "$domainName|$passwordPolicy days|$isDefault"
|
||||||
|
|
||||||
|
# Add details and failure reasons to the lists
|
||||||
|
$detailsList += $details
|
||||||
|
$failureReasonsList += $failureReasons
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare the final failure reason and details
|
||||||
|
$finalFailureReason = $failureReasonsList -join "`n"
|
||||||
|
$finalDetails = $detailsList -join "`n"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $overallResult
|
||||||
|
Status = if ($overallResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $finalDetails
|
||||||
|
FailureReason = $finalFailureReason
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,58 @@
|
|||||||
function Test-ReauthWithCode {
|
function Test-ReauthWithCode {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
try {
|
||||||
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||||
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Retrieve reauthentication settings for SharePoint Online
|
||||||
$auditResult.CISControlVer = "v8"
|
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
||||||
$auditResult.CISControl = "0.0"
|
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.10"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if (-not $isReauthenticationRestricted) {
|
||||||
$auditResult.ProfileLevel = "L1"
|
"Reauthentication with verification code does not require reauthentication within 15 days or less."
|
||||||
$auditResult.IG1 = $false
|
}
|
||||||
$auditResult.IG2 = $false
|
else {
|
||||||
$auditResult.IG3 = $false
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure reauthentication with verification code is restricted"
|
}
|
||||||
|
|
||||||
$auditResult.Result = $isReauthenticationRestricted
|
$details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
||||||
$auditResult.Details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
|
||||||
$auditResult.FailureReason = if (-not $isReauthenticationRestricted) { "Reauthentication with verification code does not require reauthentication within 15 days or less." } else { "N/A" }
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isReauthenticationRestricted
|
||||||
|
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,56 +1,72 @@
|
|||||||
function Test-ReportSecurityInTeams {
|
function Test-ReportSecurityInTeams {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.6.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.6.1 (L1) Ensure users can report security concerns in Teams
|
try {
|
||||||
|
# 8.6.1 (L1) Ensure users can report security concerns in Teams
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Retrieve the necessary settings for Teams and Exchange Online
|
||||||
# Connect to Exchange Online PowerShell using Connect-ExchangeOnline
|
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
|
||||||
|
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
||||||
|
|
||||||
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
|
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and
|
||||||
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
$ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and
|
||||||
|
$ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and
|
||||||
|
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
||||||
|
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
||||||
|
|
||||||
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and
|
# Prepare failure reasons and details based on compliance
|
||||||
$ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and
|
$failureReasons = if (-not $securityReportEnabled) {
|
||||||
$ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and
|
"Users cannot report security concerns in Teams due to one or more incorrect settings"
|
||||||
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
}
|
||||||
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
$details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
|
||||||
$auditResult = [CISAuditResult]::new()
|
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
|
||||||
$auditResult.CISControlVer = "v8"
|
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
|
||||||
$auditResult.Rec = "8.6.1"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure users can report security concerns in Teams"
|
|
||||||
$auditResult.Result = $securityReportEnabled
|
|
||||||
$auditResult.Details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
|
|
||||||
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
|
|
||||||
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
|
|
||||||
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
|
|
||||||
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
|
|
||||||
$auditResult.FailureReason = if (-not $securityReportEnabled) { "Users cannot report security concerns in Teams due to one or more incorrect settings" } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $securityReportEnabled
|
||||||
|
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,70 +1,127 @@
|
|||||||
function Test-RestrictCustomScripts {
|
function Test-RestrictCustomScripts {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
#Limit All
|
|
||||||
begin {
|
|
||||||
# .TODO Test behavior in Prod
|
|
||||||
# Dot source the class script
|
|
||||||
|
|
||||||
$auditResults = @()
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
# . .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.3.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# CIS 2.7 Ensure custom script execution is restricted on site collections
|
|
||||||
# Pass if DenyAddAndCustomizePages is set to true (Enabled). Fail otherwise.
|
|
||||||
|
|
||||||
# Get all site collections and select necessary properties
|
try {
|
||||||
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
# 7.3.4 (L1) Ensure custom script execution is restricted on site collections
|
||||||
|
|
||||||
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
|
# Retrieve all site collections and select necessary properties
|
||||||
$customScriptAllowedSites = $SPOSitesCustomScript | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
|
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
||||||
|
|
||||||
# Compliance is true if no sites allow custom scripts
|
# Replace 'sharepoint.com' with '<SPUrl>'
|
||||||
$complianceResult = $customScriptAllowedSites.Count -eq 0
|
$processedUrls = $SPOSitesCustomScript | ForEach-Object {
|
||||||
|
$_.Url = $_.Url -replace 'sharepoint\.com', '<SPUrl>'
|
||||||
|
$_
|
||||||
|
}
|
||||||
|
|
||||||
# Gather details for non-compliant sites (where custom scripts are allowed)
|
# Find sites where custom scripts are allowed
|
||||||
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object {
|
$customScriptAllowedSites = $processedUrls | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
|
||||||
"$($_.Title) ($($_.Url)): Custom Script Allowed"
|
#$verbosePreference = 'Continue'
|
||||||
|
# Check the total length of URLs
|
||||||
|
$totalUrlLength = ($customScriptAllowedSites.Url -join '').Length
|
||||||
|
Write-Verbose "Total length of URLs: $totalUrlLength"
|
||||||
|
|
||||||
|
# Extract hostnames from allowed sites if the total length exceeds the limit
|
||||||
|
$mostUsedHostname = $null
|
||||||
|
if ($totalUrlLength -gt 20000) {
|
||||||
|
Write-Verbose "Extracting hostnames from URLs..."
|
||||||
|
$hostnames = $customScriptAllowedSites.Url | ForEach-Object {
|
||||||
|
if ($_ -match '^https://([^\.]+)\.') {
|
||||||
|
$matches[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Verbose "Extracted hostnames: $($hostnames -join ', ')"
|
||||||
|
|
||||||
|
# Find the most used hostname using the Get-MostCommonWord function
|
||||||
|
$mostUsedHostname = Get-MostCommonWord -InputStrings $hostnames
|
||||||
|
Write-Verbose "Most used hostname: $mostUsedHostname"
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
|
# Compliance is true if no sites allow custom scripts
|
||||||
|
$complianceResult = $customScriptAllowedSites.Count -eq 0
|
||||||
|
|
||||||
|
# Gather details for non-compliant sites (where custom scripts are allowed)
|
||||||
|
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object {
|
||||||
|
$url = $_.Url
|
||||||
|
if ($null -ne $mostUsedHostname -and $url -match "^https://$mostUsedHostname\.<SPUrl>") {
|
||||||
|
$url = $url -replace "^https://$mostUsedHostname\.<SPUrl>", "https://<corp>.<SPUrl>"
|
||||||
|
}
|
||||||
|
"$(if ($_.Title) {$_.Title} else {"NoTitle"})|$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $complianceResult) {
|
||||||
|
"Some site collections are not restricting custom script execution. Review Details property for sites that are not aligned with the benchmark."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($complianceResult) {
|
||||||
|
"All site collections have custom script execution restricted"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Title|Url`n" + ($nonCompliantSiteDetails -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert details to PSObject and check length
|
||||||
|
$detailsPSObject = $details | ConvertFrom-Csv -Delimiter '|'
|
||||||
|
$detailsLength = ($detailsPSObject | ForEach-Object { $_.Url }).Length
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
# Create a preview of the first 10 results
|
||||||
|
$preview = $detailsPSObject | Select-Object -First 10 | ForEach-Object { "$($_.Title)|$($_.Url)" }
|
||||||
|
$details = "The output is too large. Here is a preview of the first 10 results:`n`n" + ($preview -join "`n") + "`n`nPlease run the test with the following commands to get the full details:`n`nGet-SPOSite -Limit All | Where-Object { `$.DenyAddAndCustomizePages -ne 'Enabled' } | Select-Object Title, Url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $complianceResult
|
||||||
|
Status = if ($complianceResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve the description from the test definitions
|
||||||
$auditResult = [CISAuditResult]::new()
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.CISControlVer = "v8"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.CISControl = "2.7"
|
|
||||||
$auditResult.CISDescription = "Allowlist Authorized Scripts"
|
|
||||||
$auditResult.Rec = "7.3.4"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure custom script execution is restricted on site collections"
|
|
||||||
$auditResult.Result = $complianceResult
|
|
||||||
$auditResult.Details = if (-not $complianceResult) {
|
|
||||||
$nonCompliantSiteDetails -join "; "
|
|
||||||
} else {
|
|
||||||
"All site collections have custom script execution restricted"
|
|
||||||
}
|
|
||||||
$auditResult.FailureReason = if (-not $complianceResult) {
|
|
||||||
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
|
|
||||||
} else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
$auditResult.Status = if ($complianceResult) {
|
|
||||||
"Pass"
|
|
||||||
} else {
|
|
||||||
"Fail"
|
|
||||||
}
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Measure the character count of the details
|
||||||
return $auditResults
|
#$verbosePreference = 'Continue'
|
||||||
|
$detailsLength = $details.Length
|
||||||
|
Write-Verbose "Character count of the details: $detailsLength"
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
|
# Return auditResult
|
||||||
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,58 @@
|
|||||||
function Test-RestrictExternalSharing {
|
function Test-RestrictExternalSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.3 (L1) Ensure external content sharing is restricted
|
try {
|
||||||
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
# 7.2.3 (L1) Ensure external content sharing is restricted
|
||||||
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Retrieve the SharingCapability setting for the SharePoint tenant
|
||||||
$auditResult.CISControlVer = "v8"
|
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
||||||
$auditResult.CISControl = "3.3"
|
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.3"
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.ELevel = "E3"
|
$failureReasons = if (-not $isRestricted) {
|
||||||
$auditResult.ProfileLevel = "L1"
|
"External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)"
|
||||||
$auditResult.IG1 = $true
|
}
|
||||||
$auditResult.IG2 = $true
|
else {
|
||||||
$auditResult.IG3 = $true
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure external content sharing is restricted"
|
}
|
||||||
|
|
||||||
$auditResult.Result = $isRestricted
|
$details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
||||||
$auditResult.Details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
|
||||||
$auditResult.FailureReason = if (-not $isRestricted) { "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)" } else { "N/A" }
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isRestricted
|
||||||
|
Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,89 +1,94 @@
|
|||||||
function Test-RestrictOutlookAddins {
|
function Test-RestrictOutlookAddins {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters could include credentials or other necessary data
|
# Parameters could include credentials or other necessary data
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code
|
# Initialization code
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$customPolicyFailures = @()
|
$customPolicyFailures = @()
|
||||||
$defaultPolicyFailureDetails = @()
|
$defaultPolicyFailureDetails = @()
|
||||||
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
|
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
|
||||||
|
$recnum = "6.3.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Main functionality
|
try {
|
||||||
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
||||||
|
|
||||||
# Check all mailboxes for custom policies with unallowed add-ins
|
# Check all mailboxes for custom policies with unallowed add-ins
|
||||||
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
|
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
|
||||||
|
|
||||||
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
|
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
|
||||||
foreach ($policy in $roleAssignmentPolicies) {
|
foreach ($policy in $roleAssignmentPolicies) {
|
||||||
if ($policy.RoleAssignmentPolicy) {
|
if ($policy.RoleAssignmentPolicy) {
|
||||||
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
|
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
|
||||||
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
||||||
if ($foundRoles) {
|
if ($foundRoles) {
|
||||||
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
|
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Check Default Role Assignment Policy
|
# Check Default Role Assignment Policy
|
||||||
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
|
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
|
||||||
$defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
$defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
||||||
if ($defaultPolicyRoles) {
|
if ($defaultPolicyRoles) {
|
||||||
$defaultPolicyFailureDetails = $defaultPolicyRoles
|
$defaultPolicyFailureDetails = $defaultPolicyRoles
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare result details string
|
||||||
|
$detailsString = ""
|
||||||
|
if ($customPolicyFailures) {
|
||||||
|
$detailsString += "Custom Policy Failures: | "
|
||||||
|
$detailsString += ($customPolicyFailures -join " | ")
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$detailsString += "Custom Policy Failures: None | "
|
||||||
|
}
|
||||||
|
|
||||||
|
$detailsString += "Default Role Assignment Policy: "
|
||||||
|
if ($defaultPolicyFailureDetails) {
|
||||||
|
$detailsString += ($defaultPolicyFailureDetails -join ', ')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$detailsString += "Compliant"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine result based on findings
|
||||||
|
$isCompliant = -not ($customPolicyFailures -or $defaultPolicyFailureDetails)
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isCompliant
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = $detailsString
|
||||||
|
FailureReason = if ($isCompliant) { "N/A" } else { "Unauthorized Outlook add-ins found in custom or default policies." }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Prepare result object
|
# Return the audit result
|
||||||
$auditResult.Rec = "6.3.1"
|
|
||||||
$auditResult.CISControl = "9.4"
|
|
||||||
$auditResult.CISDescription = "Restrict Unnecessary or Unauthorized Browser and Email Client Extensions"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure users installing Outlook add-ins is not allowed"
|
|
||||||
|
|
||||||
$detailsString = ""
|
|
||||||
if ($customPolicyFailures) {
|
|
||||||
$detailsString += "Custom Policy Failures: | "
|
|
||||||
# Use pipes or tabs here instead of newlines
|
|
||||||
$detailsString += ($customPolicyFailures -join " | ")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$detailsString += "Custom Policy Failures: None | "
|
|
||||||
}
|
|
||||||
|
|
||||||
$detailsString += "Default Role Assignment Policy: "
|
|
||||||
if ($defaultPolicyFailureDetails) {
|
|
||||||
$detailsString += ($defaultPolicyFailureDetails -join ', ')
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$detailsString += "Compliant"
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($customPolicyFailures -or $defaultPolicyFailureDetails) {
|
|
||||||
$auditResult.Result = $false
|
|
||||||
$auditResult.Status = "Fail"
|
|
||||||
$auditResult.Details = $detailsString
|
|
||||||
$auditResult.FailureReason = "Unauthorized Outlook add-ins found in custom or default policies."
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$auditResult.Result = $true
|
|
||||||
$auditResult.Status = "Pass"
|
|
||||||
$auditResult.Details = "No unauthorized Outlook add-ins found in custom or default policies."
|
|
||||||
$auditResult.FailureReason = "N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Return auditResult
|
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,48 +1,72 @@
|
|||||||
function Test-RestrictStorageProvidersOutlook {
|
function Test-RestrictStorageProvidersOutlook {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added here if needed
|
# Parameters can be added here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.5.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
try {
|
||||||
$owaPolicies = Get-OwaMailboxPolicy
|
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
||||||
$allPoliciesRestricted = $owaPolicies | ForEach-Object { $_.AdditionalStorageProvidersAvailable } | ForEach-Object { -not $_ }
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve all OwaMailbox policies
|
||||||
$auditResult.CISControlVer = "v8"
|
$owaPolicies = Get-OwaMailboxPolicy
|
||||||
$auditResult.CISControl = "3.3"
|
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable }
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
|
||||||
$auditResult.Rec = "6.5.3"
|
# Determine compliance
|
||||||
$auditResult.ELevel = "E3" # Based on your environment
|
$allPoliciesRestricted = $nonCompliantPolicies.Count -eq 0
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.IG1 = $true
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.IG2 = $true
|
$failureReasons = if ($allPoliciesRestricted) {
|
||||||
$auditResult.IG3 = $true
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure additional storage providers are restricted in Outlook on the web"
|
}
|
||||||
$auditResult.Result = $allPoliciesRestricted
|
else {
|
||||||
$auditResult.Details = if($allPoliciesRestricted) {
|
"One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable."
|
||||||
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
|
}
|
||||||
} else {
|
|
||||||
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable } | Select-Object -ExpandProperty Name
|
$details = if ($allPoliciesRestricted) {
|
||||||
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies -join ', ')"
|
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies.Name -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allPoliciesRestricted
|
||||||
|
Status = if ($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult.FailureReason = if(-not $allPoliciesRestricted) { "One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable." } else { "N/A" }
|
|
||||||
$auditResult.Status = if($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Additional helper functions (if any)
|
# Additional helper functions (if any)
|
||||||
|
|
||||||
|
@@ -1,43 +1,64 @@
|
|||||||
function Test-RestrictTenantCreation {
|
function Test-RestrictTenantCreation {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "5.1.2.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
try {
|
||||||
# Pass if AllowedToCreateTenants is False. Fail otherwise.
|
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
||||||
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
|
|
||||||
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve the tenant creation policy
|
||||||
$auditResult = [CISAuditResult]::new()
|
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
|
||||||
$auditResult.Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "5.1.2.3"
|
|
||||||
$auditResult.RecDescription = "Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "0.0"
|
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.Result = $tenantCreationResult
|
|
||||||
$auditResult.Details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
|
|
||||||
$auditResult.FailureReason = if (-not $tenantCreationResult) { "Non-admin users can create tenants" } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if ($tenantCreationResult) {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Non-admin users can create tenants"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $tenantCreationResult
|
||||||
|
Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional helper functions (if any)
|
||||||
|
@@ -1,49 +1,69 @@
|
|||||||
function Test-SafeAttachmentsPolicy {
|
function Test-SafeAttachmentsPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResults = @()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Retrieve all Safe Attachment policies where Enable is set to True
|
try {
|
||||||
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
|
# 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
||||||
|
|
||||||
# If there are any enabled policies, the result is Pass. If not, it's Fail.
|
# Retrieve all Safe Attachment policies where Enable is set to True
|
||||||
$result = $safeAttachmentPolicies -ne $null -and $safeAttachmentPolicies.Count -gt 0
|
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
|
||||||
$details = if ($result) {
|
|
||||||
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
# Determine result and details based on the presence of enabled policies
|
||||||
} else {
|
$result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
|
||||||
"No Safe Attachments Policies are enabled."
|
$details = if ($result) {
|
||||||
|
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"No Safe Attachments Policies are enabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
$failureReasons = if ($result) {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Safe Attachments policy is not enabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
$failureReason = if ($result) { "N/A" } else { "Safe Attachments policy is not enabled." }
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve the description from the test definitions
|
||||||
$auditResult = [CISAuditResult]::new()
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.ELevel = "E5"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.Rec = "2.1.4"
|
|
||||||
$auditResult.RecDescription = "Ensure Safe Attachments policy is enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "9.7"
|
|
||||||
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $result
|
|
||||||
$auditResult.Details = $details
|
|
||||||
$auditResult.FailureReason = $failureReason
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional helper functions (if any)
|
||||||
|
@@ -1,57 +1,76 @@
|
|||||||
function Test-SafeAttachmentsTeams {
|
function Test-SafeAttachmentsTeams {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
$auditResults = @()
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Requires E5 license
|
try {
|
||||||
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
|
# 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
|
||||||
$atpPolicies = Get-AtpPolicyForO365
|
|
||||||
|
|
||||||
# Check if the required ATP policies are enabled
|
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
|
||||||
$atpPolicyResult = $atpPolicies | Where-Object {
|
$atpPolicies = Get-AtpPolicyForO365
|
||||||
$_.EnableATPForSPOTeamsODB -eq $true -and
|
|
||||||
$_.EnableSafeDocs -eq $true -and
|
# Check if the required ATP policies are enabled
|
||||||
$_.AllowSafeDocsOpen -eq $false
|
$atpPolicyResult = $atpPolicies | Where-Object {
|
||||||
|
$_.EnableATPForSPOTeamsODB -eq $true -and
|
||||||
|
$_.EnableSafeDocs -eq $true -and
|
||||||
|
$_.AllowSafeDocsOpen -eq $false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine the result based on the ATP policy settings
|
||||||
|
$result = $null -ne $atpPolicyResult
|
||||||
|
$details = if ($result) {
|
||||||
|
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
||||||
|
}
|
||||||
|
|
||||||
|
$failureReasons = if ($result) {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Determine the result based on the ATP policy settings
|
# Retrieve the description from the test definitions
|
||||||
$result = $null -ne $atpPolicyResult
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$details = if ($result) {
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
|
||||||
} else {
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$failureReason = if ($result) { "N/A" } else { "ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured." }
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E5"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.Rec = "2.1.5"
|
|
||||||
$auditResult.RecDescription = "Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "9.7, 10.1"
|
|
||||||
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $result
|
|
||||||
$auditResult.Details = $details
|
|
||||||
$auditResult.FailureReason = $failureReason
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional helper functions (if any)
|
||||||
|
@@ -1,70 +1,81 @@
|
|||||||
function Test-SafeLinksOfficeApps {
|
function Test-SafeLinksOfficeApps {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Retrieve all Safe Links policies
|
try {
|
||||||
$policies = Get-SafeLinksPolicy
|
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||||
|
|
||||||
# Initialize the details collection
|
# Retrieve all Safe Links policies
|
||||||
$misconfiguredDetails = @()
|
$policies = Get-SafeLinksPolicy
|
||||||
|
|
||||||
foreach ($policy in $policies) {
|
# Initialize the details collection
|
||||||
# Get the detailed configuration of each policy
|
$misconfiguredDetails = @()
|
||||||
$policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
|
|
||||||
|
|
||||||
# Check each required property and record failures
|
foreach ($policy in $policies) {
|
||||||
$failures = @()
|
# Get the detailed configuration of each policy
|
||||||
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" }
|
$policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
|
||||||
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" }
|
|
||||||
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" }
|
|
||||||
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" }
|
|
||||||
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" }
|
|
||||||
if ($policyDetails.ScanUrls -ne $true) { $failures += "ScanUrls: False" }
|
|
||||||
if ($policyDetails.EnableForInternalSenders -ne $true) { $failures += "EnableForInternalSenders: False" }
|
|
||||||
if ($policyDetails.DeliverMessageAfterScan -ne $true) { $failures += "DeliverMessageAfterScan: False" }
|
|
||||||
if ($policyDetails.DisableUrlRewrite -ne $false) { $failures += "DisableUrlRewrite: True" }
|
|
||||||
|
|
||||||
# Only add details for policies that have misconfigurations
|
# Check each required property and record failures
|
||||||
if ($failures.Count -gt 0) {
|
$failures = @()
|
||||||
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" }
|
||||||
|
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" }
|
||||||
|
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" }
|
||||||
|
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" }
|
||||||
|
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" }
|
||||||
|
if ($policyDetails.ScanUrls -ne $true) { $failures += "ScanUrls: False" }
|
||||||
|
if ($policyDetails.EnableForInternalSenders -ne $true) { $failures += "EnableForInternalSenders: False" }
|
||||||
|
if ($policyDetails.DeliverMessageAfterScan -ne $true) { $failures += "DeliverMessageAfterScan: False" }
|
||||||
|
if ($policyDetails.DisableUrlRewrite -ne $false) { $failures += "DisableUrlRewrite: True" }
|
||||||
|
|
||||||
|
# Only add details for policies that have misconfigurations
|
||||||
|
if ($failures.Count -gt 0) {
|
||||||
|
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Prepare the final result
|
||||||
|
$result = $misconfiguredDetails.Count -eq 0
|
||||||
|
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
||||||
|
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Prepare the final result
|
# Retrieve the description from the test definitions
|
||||||
$result = $misconfiguredDetails.Count -eq 0
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create the audit result object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E5"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.Rec = "2.1.1"
|
|
||||||
$auditResult.RecDescription = "Ensure Safe Links for Office Applications is Enabled"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "10.1"
|
|
||||||
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $result
|
|
||||||
$auditResult.Details = $details
|
|
||||||
$auditResult.FailureReason = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,47 @@
|
|||||||
function Test-SharePointAADB2B {
|
function Test-SharePointAADB2B {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "7.2.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
try {
|
||||||
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
||||||
|
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Populate the auditResult object with the required properties
|
||||||
$auditResult.CISControlVer = "v8"
|
$params = @{
|
||||||
$auditResult.CISControl = "0.0"
|
Rec = $recnum
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
||||||
|
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
||||||
|
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
||||||
|
FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.2"
|
# Retrieve the description from the test definitions
|
||||||
$auditResult.ELevel = "E3"
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.ProfileLevel = "L1"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.RecDescription = "Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled"
|
|
||||||
|
|
||||||
$auditResult.Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$auditResult.Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
|
||||||
$auditResult.FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
$auditResult.Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,37 +1,48 @@
|
|||||||
function Test-SharePointExternalSharingDomains {
|
function Test-SharePointExternalSharingDomains {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "7.2.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
|
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
||||||
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
|
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
|
||||||
|
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Populate the auditResult object with the required properties
|
||||||
$auditResult.CISControlVer = "v8"
|
$params = @{
|
||||||
$auditResult.CISControl = "3.3"
|
Rec = $recnum
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
Result = $isDomainRestrictionConfigured
|
||||||
|
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
||||||
|
Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
|
||||||
|
FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.6"
|
# Retrieve the description from the test definitions
|
||||||
$auditResult.ELevel = "E3"
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.ProfileLevel = "L2"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure SharePoint external sharing is managed through domain whitelist/blacklists"
|
|
||||||
|
|
||||||
$auditResult.Result = $isDomainRestrictionConfigured
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$auditResult.Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
|
|
||||||
$auditResult.FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
$auditResult.Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,37 +1,48 @@
|
|||||||
function Test-SharePointGuestsItemSharing {
|
function Test-SharePointGuestsItemSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Initialization code
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "7.2.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
|
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
||||||
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
|
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
|
||||||
|
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Populate the auditResult object with the required properties
|
||||||
$auditResult.CISControlVer = "v8"
|
$params = @{
|
||||||
$auditResult.CISControl = "3.3"
|
Rec = $recnum
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
Result = $isGuestResharingPrevented
|
||||||
|
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
||||||
|
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
||||||
|
FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.5"
|
# Retrieve the description from the test definitions
|
||||||
$auditResult.ELevel = "E3"
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.ProfileLevel = "L2"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure that SharePoint guest users cannot share items they don't own"
|
|
||||||
|
|
||||||
$auditResult.Result = $isGuestResharingPrevented
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$auditResult.Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
|
||||||
$auditResult.FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
$auditResult.Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,56 +1,67 @@
|
|||||||
function Test-SpamPolicyAdminNotify {
|
function Test-SpamPolicyAdminNotify {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResults = @()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "2.1.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Get the default hosted outbound spam filter policy
|
try {
|
||||||
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
|
||||||
|
|
||||||
# Check if both settings are enabled
|
# Get the default hosted outbound spam filter policy
|
||||||
$bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail
|
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
||||||
$notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
|
|
||||||
$areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
|
|
||||||
|
|
||||||
# Prepare failure details if any setting is not enabled
|
# Check if both settings are enabled
|
||||||
$failureDetails = @()
|
$bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail
|
||||||
if (-not $bccSuspiciousOutboundMailEnabled) {
|
$notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
|
||||||
$failureDetails += "BccSuspiciousOutboundMail is not enabled."
|
$areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
|
||||||
|
|
||||||
|
# Prepare failure details if any setting is not enabled
|
||||||
|
$failureDetails = @()
|
||||||
|
if (-not $bccSuspiciousOutboundMailEnabled) {
|
||||||
|
$failureDetails += "BccSuspiciousOutboundMail is not enabled."
|
||||||
|
}
|
||||||
|
if (-not $notifyOutboundSpamEnabled) {
|
||||||
|
$failureDetails += "NotifyOutboundSpam is not enabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create an instance of CISAuditResult and populate it
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $areSettingsEnabled
|
||||||
|
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
|
||||||
|
FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
if (-not $notifyOutboundSpamEnabled) {
|
catch {
|
||||||
$failureDetails += "NotifyOutboundSpam is not enabled."
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.Rec = "2.1.6"
|
|
||||||
$auditResult.RecDescription = "Ensure Exchange Online Spam Policies are set to notify administrators"
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "17.5"
|
|
||||||
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.Result = $areSettingsEnabled
|
|
||||||
$auditResult.Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
|
|
||||||
$auditResult.FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,52 +1,62 @@
|
|||||||
function Test-TeamsExternalAccess {
|
function Test-TeamsExternalAccess {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResults = @()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "8.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
|
try {
|
||||||
|
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$externalAccessConfig = Get-CsTenantFederationConfiguration
|
$externalAccessConfig = Get-CsTenantFederationConfiguration
|
||||||
|
|
||||||
$allowedDomainsLimited = $false
|
$allowedDomainsLimited = $false
|
||||||
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
|
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
|
||||||
$allowedDomainsLimited = $true
|
$allowedDomainsLimited = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if the configurations are as recommended
|
||||||
|
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
||||||
|
|
||||||
|
# Create an instance of CISAuditResult and populate it
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isCompliant
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
|
||||||
|
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Check if the configurations are as recommended
|
# Retrieve the description from the test definitions
|
||||||
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
|
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
|
||||||
$auditResult.Rec = "8.2.1"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
|
||||||
$auditResult.RecDescription = "Ensure 'external access' is restricted in the Teams admin center"
|
|
||||||
$auditResult.Result = $isCompliant
|
|
||||||
$auditResult.Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
|
|
||||||
$auditResult.FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,56 +1,66 @@
|
|||||||
function Test-TeamsExternalFileSharing {
|
function Test-TeamsExternalFileSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# Parameters can be added here if needed
|
# Parameters can be added here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResults = @()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "8.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services
|
try {
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# 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
|
||||||
|
|
||||||
# Assuming that 'approvedProviders' is a list of approved cloud storage service names
|
# 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
|
# This list must be defined according to your organization's approved cloud storage services
|
||||||
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
|
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
|
||||||
$clientConfig = Get-CsTeamsClientConfiguration
|
$clientConfig = Get-CsTeamsClientConfiguration
|
||||||
|
|
||||||
$isCompliant = $true
|
$isCompliant = $true
|
||||||
$nonCompliantProviders = @()
|
$nonCompliantProviders = @()
|
||||||
|
|
||||||
foreach ($provider in $approvedProviders) {
|
foreach ($provider in $approvedProviders) {
|
||||||
if (-not $clientConfig.$provider) {
|
if (-not $clientConfig.$provider) {
|
||||||
$isCompliant = $false
|
$isCompliant = $false
|
||||||
$nonCompliantProviders += $provider
|
$nonCompliantProviders += $provider
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Create an instance of CISAuditResult and populate it
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isCompliant
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
||||||
|
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve the description from the test definitions
|
||||||
$auditResult = [CISAuditResult]::new()
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$auditResult.CISControlVer = "v8"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$auditResult.CISControl = "3.3"
|
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
|
||||||
$auditResult.Rec = "8.1.1"
|
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L2"
|
|
||||||
$auditResult.IG1 = $true # Set based on the benchmark
|
|
||||||
$auditResult.IG2 = $true # Set based on the benchmark
|
|
||||||
$auditResult.IG3 = $true # Set based on the benchmark
|
|
||||||
$auditResult.RecDescription = "Ensure external file sharing in Teams is enabled for only approved cloud storage services"
|
|
||||||
$auditResult.Result = $isCompliant
|
|
||||||
$auditResult.Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
|
||||||
$auditResult.FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
|
||||||
|
|
||||||
$auditResults += $auditResult
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-MostCommonWord.tests.ps1
Normal file
27
tests/Unit/Private/Get-MostCommonWord.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-RequiredModule.tests.ps1
Normal file
27
tests/Unit/Private/Get-RequiredModule.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-TestDefinitionsObject.tests.ps1
Normal file
27
tests/Unit/Private/Get-TestDefinitionsObject.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Initialize-CISAuditResult.tests.ps1
Normal file
27
tests/Unit/Private/Initialize-CISAuditResult.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Invoke-TestFunction.tests.ps1
Normal file
27
tests/Unit/Private/Invoke-TestFunction.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Measure-AuditResult.tests.ps1
Normal file
27
tests/Unit/Private/Measure-AuditResult.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/New-MergedObject.tests.ps1
Normal file
27
tests/Unit/Private/New-MergedObject.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Test-IsAdmin.tests.ps1
Normal file
27
tests/Unit/Private/Test-IsAdmin.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user