Compare commits
97 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
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 |
49
.github/workflows/powershell.yml
vendored
Normal file
49
.github/workflows/powershell.yml
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# 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: .\
|
||||||
|
recurse: true
|
||||||
|
# Include your own basic security rules. Removing this option will run all the rules
|
||||||
|
includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText"'
|
||||||
|
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"
|
||||||
|
]
|
||||||
|
}
|
73
CHANGELOG.md
73
CHANGELOG.md
@@ -6,6 +6,79 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
|
|
||||||
### Added
|
### 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.
|
||||||
|
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])
|
92
README.md
92
README.md
@@ -10,6 +10,43 @@ This PowerShell module is based on CIS benchmarks and is distributed under the C
|
|||||||
|
|
||||||
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).
|
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)
|
||||||
|
## Get-AdminRoleUserLicense
|
||||||
|
### Synopsis
|
||||||
|
Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API.
|
||||||
|
### Syntax
|
||||||
|
```powershell
|
||||||
|
Get-AdminRoleUserLicense [-SkipGraphConnection] [<CommonParameters>]
|
||||||
|
```
|
||||||
|
### Parameters
|
||||||
|
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
|
||||||
|
| - | - | - | - | - | - |
|
||||||
|
| <nobr>SkipGraphConnection</nobr> | | 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. | false | false | False |
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### Note
|
||||||
|
Creation Date: 2024-04-15 Purpose/Change: Initial function development to support Microsoft 365 administrative role auditing.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
**EXAMPLE 1**
|
||||||
|
```powershell
|
||||||
|
Get-AdminRoleUserLicense
|
||||||
|
```
|
||||||
|
This example retrieves all administrative role users along with their licenses by connecting to Microsoft Graph using the default scopes.
|
||||||
|
|
||||||
|
**EXAMPLE 2**
|
||||||
|
```powershell
|
||||||
|
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.
|
||||||
|
|
||||||
|
### Links
|
||||||
|
|
||||||
|
- [https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense](https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense)
|
||||||
## Invoke-M365SecurityAudit
|
## Invoke-M365SecurityAudit
|
||||||
### Synopsis
|
### Synopsis
|
||||||
Invokes a security audit for Microsoft 365 environments.
|
Invokes a security audit for Microsoft 365 environments.
|
||||||
@@ -29,18 +66,14 @@ Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-Include
|
|||||||
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-IncludeRecommendation <String[]>] [-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>]
|
Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-SkipRecommendation <String[]>] [-DoNotConnect] [-DoNotDisconnect] [-NoModuleCheck] [-WhatIf] [-Confirm] [<CommonParameters>]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
### Parameters
|
### Parameters
|
||||||
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
|
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
|
||||||
| - | - | - | - | - | - |
|
| - | - | - | - | - | - |
|
||||||
| <nobr>TenantAdminUrl</nobr> | | The URL of the tenant admin. This parameter is mandatory. | true | false | |
|
| <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>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>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>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>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>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>IncludeIG3</nobr> | | If specified, includes tests where IG3 is true. | false | false | False |
|
||||||
@@ -55,10 +88,10 @@ Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-SkipRec
|
|||||||
- None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
- None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
||||||
|
|
||||||
### Outputs
|
### Outputs
|
||||||
- CISAuditResult\\[\] The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
- CISAuditResult\[\] The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
||||||
|
|
||||||
### Note
|
### 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
|
- This module is based on CIS benchmarks. - 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. - 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 - Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
**EXAMPLE 1**
|
**EXAMPLE 1**
|
||||||
@@ -88,4 +121,45 @@ Captures the audit results into a variable and exports them to a CSV file.
|
|||||||
|
|
||||||
### Links
|
### Links
|
||||||
|
|
||||||
- [Online Version: [GitHub Repository URL]](#Online Version: [GitHub Repository URL])
|
- [https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit](https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit)
|
||||||
|
## Sync-CISExcelAndCsvData
|
||||||
|
### Synopsis
|
||||||
|
Synchronizes data between an Excel file and a CSV file and optionally updates the Excel worksheet.
|
||||||
|
### Syntax
|
||||||
|
```powershell
|
||||||
|
Sync-CISExcelAndCsvData [-ExcelPath] <String> [-WorksheetName] <String> [-CsvPath] <String> [-SkipUpdate] [<CommonParameters>]
|
||||||
|
```
|
||||||
|
### Parameters
|
||||||
|
| Name | Alias | Description | Required? | Pipeline Input | Default Value |
|
||||||
|
| - | - | - | - | - | - |
|
||||||
|
| <nobr>ExcelPath</nobr> | | The path to the Excel file that contains the original data. This parameter is mandatory. | true | false | |
|
||||||
|
| <nobr>WorksheetName</nobr> | | The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory. | true | false | |
|
||||||
|
| <nobr>CsvPath</nobr> | | The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory. | true | false | |
|
||||||
|
| <nobr>SkipUpdate</nobr> | | If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data. | false | false | False |
|
||||||
|
### Inputs
|
||||||
|
- None. You cannot pipe objects to Sync-CISExcelAndCsvData.
|
||||||
|
|
||||||
|
### Outputs
|
||||||
|
- Object\[\] If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
|
||||||
|
|
||||||
|
### Note
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
**EXAMPLE 1**
|
||||||
|
```powershell
|
||||||
|
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 2**
|
||||||
|
```powershell
|
||||||
|
$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.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
### Links
|
||||||
|
|
||||||
|
- [https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData](https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData)
|
||||||
|
201
docs/index.html
201
docs/index.html
@@ -2,7 +2,7 @@
|
|||||||
<!--
|
<!--
|
||||||
<auto-generated>
|
<auto-generated>
|
||||||
<synopsis>
|
<synopsis>
|
||||||
This code was generated by a tool. on: 03/25/2024 08:16:49
|
This code was generated by a tool. on: 05/28/2024 20:45:16
|
||||||
</synopsis>
|
</synopsis>
|
||||||
<description>
|
<description>
|
||||||
If you'd like to regenerate the documentation, please open up powershell and run
|
If you'd like to regenerate the documentation, please open up powershell and run
|
||||||
@@ -97,13 +97,95 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul class="nav navbar-nav list-group" id="searchList">
|
<ul class="nav navbar-nav list-group" id="searchList">
|
||||||
|
<li class="nav-menu list-group-item"><a href="#Get-AdminRoleUserLicense">Get-AdminRoleUserLicense</a></li>
|
||||||
<li class="nav-menu list-group-item"><a href="#Invoke-M365SecurityAudit">Invoke-M365SecurityAudit</a></li>
|
<li class="nav-menu list-group-item"><a href="#Invoke-M365SecurityAudit">Invoke-M365SecurityAudit</a></li>
|
||||||
|
<li class="nav-menu list-group-item"><a href="#Sync-CISExcelAndCsvData">Sync-CISExcelAndCsvData</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!--/.nav-collapse -->
|
</div><!--/.nav-collapse -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-9 col-md-8 col-sm-7 col-xs-12">
|
<div class="col-lg-9 col-md-8 col-sm-7 col-xs-12">
|
||||||
|
<div id="Get-AdminRoleUserLicense" class="toggle_container">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2> Get-AdminRoleUserLicense </h2>
|
||||||
|
<p>Retrieves user licenses and roles for administrative accounts from Microsoft 365 via the Graph API.</p>
|
||||||
|
<p>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.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Syntax </h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class='panel-body'>
|
||||||
|
<pre class="brush: ps">Get-AdminRoleUserLicense [-SkipGraphConnection] [<CommonParameters>]</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Parameters </h3>
|
||||||
|
<table class="table table-striped table-bordered table-condensed visible-on">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th class="visible-lg visible-md">Alias</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th class="visible-lg visible-md">Required?</th>
|
||||||
|
<th class="visible-lg">Pipeline Input</th>
|
||||||
|
<th class="visible-lg">Default Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><nobr>-SkipGraphConnection</nobr></td>
|
||||||
|
<td class="visible-lg visible-md"></td>
|
||||||
|
<td>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.</td>
|
||||||
|
<td class="visible-lg visible-md">false</td>
|
||||||
|
<td class="visible-lg">false</td>
|
||||||
|
<td class="visible-lg">False</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Inputs </h3>
|
||||||
|
<p>The input type is the type of the objects that you can pipe to the cmdlet.</p>
|
||||||
|
<ul><li>None. You cannot pipe objects to Get-AdminRoleUserLicense.</li></ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Outputs </h3>
|
||||||
|
<p>The output type is the type of the objects that the cmdlet emits.</p>
|
||||||
|
<ul><li>PSCustomObject
|
||||||
|
Returns a custom object for each user with administrative roles that includes the following properties: RoleName, UserName, UserPrincipalName, UserId, HybridUser, and Licenses.</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class='panel panel-default'>
|
||||||
|
<div class='panel-heading'>
|
||||||
|
<h3 class='panel-title'> Note </h3>
|
||||||
|
</div>
|
||||||
|
<div class='panel-body'>Creation Date: 2024-04-15
|
||||||
|
Purpose/Change: Initial function development to support Microsoft 365 administrative role auditing.</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Examples </h3>
|
||||||
|
</div>
|
||||||
|
<div class='panel panel-default'>
|
||||||
|
<div class='panel-body'>
|
||||||
|
<strong>EXAMPLE 1</strong>
|
||||||
|
<pre class="brush: ps">Get-AdminRoleUserLicense</pre>
|
||||||
|
<div>This example retrieves all administrative role users along with their licenses by connecting to Microsoft Graph using the default scopes.</div>
|
||||||
|
<strong>EXAMPLE 2</strong>
|
||||||
|
<pre class="brush: ps">Get-AdminRoleUserLicense -SkipGraphConnection</pre>
|
||||||
|
<div>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.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class='pull-right'><a onclick='document.body.scrollTop = document.documentElement.scrollTop = 0;' style='cursor: pointer;'>Top of page</a>
|
||||||
|
<div>
|
||||||
|
<h3> Links </h3>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li class=''><a href='https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense' target='_blank'>https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="Invoke-M365SecurityAudit" class="toggle_container">
|
<div id="Invoke-M365SecurityAudit" class="toggle_container">
|
||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2> Invoke-M365SecurityAudit </h2>
|
<h2> Invoke-M365SecurityAudit </h2>
|
||||||
@@ -274,8 +356,12 @@ The cmdlet returns an array of CISAuditResult objects representing the results o
|
|||||||
<div class='panel-heading'>
|
<div class='panel-heading'>
|
||||||
<h3 class='panel-title'> Note </h3>
|
<h3 class='panel-title'> Note </h3>
|
||||||
</div>
|
</div>
|
||||||
<div class='panel-body'>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:
|
<div class='panel-body'>- This module is based on CIS benchmarks.
|
||||||
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en</div>
|
- 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.
|
||||||
|
- 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
|
||||||
|
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h3> Examples </h3>
|
<h3> Examples </h3>
|
||||||
@@ -302,7 +388,114 @@ PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation</p
|
|||||||
<h3> Links </h3>
|
<h3> Links </h3>
|
||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
<li class='psLink'><a href='#Online Version: [GitHub Repository URL]' target='_top'>Online Version: [GitHub Repository URL]</a></li>
|
<li class=''><a href='https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit' target='_blank'>https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="Sync-CISExcelAndCsvData" class="toggle_container">
|
||||||
|
<div class="page-header">
|
||||||
|
<h2> Sync-CISExcelAndCsvData </h2>
|
||||||
|
<p>Synchronizes data between an Excel file and a CSV file and optionally updates the Excel worksheet.</p>
|
||||||
|
<p>The Sync-CISExcelAndCsvData function merges data from a specified Excel file and a CSV file based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file while preserving the original formatting and structure of the Excel worksheet.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Syntax </h3>
|
||||||
|
</div>
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class='panel-body'>
|
||||||
|
<pre class="brush: ps">Sync-CISExcelAndCsvData [-ExcelPath] <String> [-WorksheetName] <String> [-CsvPath] <String> [-SkipUpdate] [<CommonParameters>]</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Parameters </h3>
|
||||||
|
<table class="table table-striped table-bordered table-condensed visible-on">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th class="visible-lg visible-md">Alias</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th class="visible-lg visible-md">Required?</th>
|
||||||
|
<th class="visible-lg">Pipeline Input</th>
|
||||||
|
<th class="visible-lg">Default Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td><nobr>-ExcelPath</nobr></td>
|
||||||
|
<td class="visible-lg visible-md"></td>
|
||||||
|
<td>The path to the Excel file that contains the original data. This parameter is mandatory.</td>
|
||||||
|
<td class="visible-lg visible-md">true</td>
|
||||||
|
<td class="visible-lg">false</td>
|
||||||
|
<td class="visible-lg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><nobr>-WorksheetName</nobr></td>
|
||||||
|
<td class="visible-lg visible-md"></td>
|
||||||
|
<td>The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.</td>
|
||||||
|
<td class="visible-lg visible-md">true</td>
|
||||||
|
<td class="visible-lg">false</td>
|
||||||
|
<td class="visible-lg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><nobr>-CsvPath</nobr></td>
|
||||||
|
<td class="visible-lg visible-md"></td>
|
||||||
|
<td>The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory.</td>
|
||||||
|
<td class="visible-lg visible-md">true</td>
|
||||||
|
<td class="visible-lg">false</td>
|
||||||
|
<td class="visible-lg"></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><nobr>-SkipUpdate</nobr></td>
|
||||||
|
<td class="visible-lg visible-md"></td>
|
||||||
|
<td>If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.</td>
|
||||||
|
<td class="visible-lg visible-md">false</td>
|
||||||
|
<td class="visible-lg">false</td>
|
||||||
|
<td class="visible-lg">False</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Inputs </h3>
|
||||||
|
<p>The input type is the type of the objects that you can pipe to the cmdlet.</p>
|
||||||
|
<ul><li>None. You cannot pipe objects to Sync-CISExcelAndCsvData.</li></ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Outputs </h3>
|
||||||
|
<p>The output type is the type of the objects that the cmdlet emits.</p>
|
||||||
|
<ul><li>Object[]
|
||||||
|
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.</li></ul>
|
||||||
|
</div>
|
||||||
|
<div class='panel panel-default'>
|
||||||
|
<div class='panel-heading'>
|
||||||
|
<h3 class='panel-title'> Note </h3>
|
||||||
|
</div>
|
||||||
|
<div class='panel-body'>- 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.</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3> Examples </h3>
|
||||||
|
</div>
|
||||||
|
<div class='panel panel-default'>
|
||||||
|
<div class='panel-body'>
|
||||||
|
<strong>EXAMPLE 1</strong>
|
||||||
|
<pre class="brush: 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.</pre>
|
||||||
|
<div></div>
|
||||||
|
<strong>EXAMPLE 2</strong>
|
||||||
|
<pre class="brush: 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.</pre>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p class='pull-right'><a onclick='document.body.scrollTop = document.documentElement.scrollTop = 0;' style='cursor: pointer;'>Top of page</a>
|
||||||
|
<div>
|
||||||
|
<h3> Links </h3>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li class=''><a href='https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData' target='_blank'>https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
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,17 @@
|
|||||||
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.3"
|
||||||
|
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
|
||||||
|
#>
|
||||||
|
|
||||||
|
|
||||||
|
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,56 +1,58 @@
|
|||||||
function Connect-M365Suite {
|
function Connect-M365Suite {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
# Parameter to specify the SharePoint Online Tenant Admin URL
|
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string]$TenantAdminUrl
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string[]]$RequiredConnections
|
||||||
)
|
)
|
||||||
$VerbosePreference = "SilentlyContinue"
|
|
||||||
|
$VerbosePreference = "SilentlyContinue"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
|
||||||
|
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") {
|
||||||
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") {
|
||||||
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,59 @@
|
|||||||
function Disconnect-M365Suite {
|
function Disconnect-M365Suite {
|
||||||
|
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
|
||||||
}
|
}
|
63
source/Private/Get-TestDefinitionsObject.ps1
Normal file
63
source/Private/Get-TestDefinitionsObject.ps1
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
function Get-TestDefinitionsObject {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[object[]]$TestDefinitions,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ParameterSetName,
|
||||||
|
|
||||||
|
[string]$ELevel,
|
||||||
|
[string]$ProfileLevel,
|
||||||
|
[string[]]$IncludeRecommendation,
|
||||||
|
[string[]]$SkipRecommendation
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Verbose "Initial test definitions count: $($TestDefinitions.Count)"
|
||||||
|
|
||||||
|
switch ($ParameterSetName) {
|
||||||
|
'ELevelFilter' {
|
||||||
|
Write-Verbose "Applying ELevelFilter"
|
||||||
|
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
|
||||||
|
Write-Verbose "Filtering on ELevel = $ELevel and ProfileLevel = $ProfileLevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($null -ne $ELevel) {
|
||||||
|
Write-Verbose "Filtering on ELevel = $ELevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ELevel -eq $ELevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($null -ne $ProfileLevel) {
|
||||||
|
Write-Verbose "Filtering on ProfileLevel = $ProfileLevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ProfileLevel -eq $ProfileLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'IG1Filter' {
|
||||||
|
Write-Verbose "Applying IG1Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'IG2Filter' {
|
||||||
|
Write-Verbose "Applying IG2Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'IG3Filter' {
|
||||||
|
Write-Verbose "Applying IG3Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'RecFilter' {
|
||||||
|
Write-Verbose "Applying RecFilter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
|
||||||
|
}
|
||||||
|
'SkipRecFilter' {
|
||||||
|
Write-Verbose "Applying SkipRecFilter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
||||||
|
return $TestDefinitions
|
||||||
|
}
|
45
source/Private/Initialize-CISAuditResult.ps1
Normal file
45
source/Private/Initialize-CISAuditResult.ps1
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
function Initialize-CISAuditResult {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Rec,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[bool]$Result,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Status,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$Details,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$FailureReason
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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 }
|
||||||
|
|
||||||
|
# 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'
|
||||||
|
$auditResult.Result = $Result
|
||||||
|
$auditResult.Status = $Status
|
||||||
|
$auditResult.Details = $Details
|
||||||
|
$auditResult.FailureReason = $FailureReason
|
||||||
|
|
||||||
|
return $auditResult
|
||||||
|
}
|
46
source/Private/Merge-CISExcelAndCsvData.ps1
Normal file
46
source/Private/Merge-CISExcelAndCsvData.ps1
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
function Merge-CISExcelAndCsvData {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ExcelPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$WorksheetName,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$CsvPath
|
||||||
|
)
|
||||||
|
|
||||||
|
process {
|
||||||
|
# Import data from Excel and CSV
|
||||||
|
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
|
||||||
|
$csvData = Import-Csv -Path $CsvPath
|
||||||
|
|
||||||
|
# Define a function to create a merged object
|
||||||
|
function CreateMergedObject($excelItem, $csvRow) {
|
||||||
|
$newObject = New-Object PSObject
|
||||||
|
|
||||||
|
foreach ($property in $excelItem.PSObject.Properties) {
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
|
||||||
|
}
|
||||||
|
$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
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over each item in the imported Excel object and merge with CSV data
|
||||||
|
$mergedData = foreach ($item in $import) {
|
||||||
|
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
|
||||||
|
if ($csvRow) {
|
||||||
|
CreateMergedObject -excelItem $item -csvRow $csvRow
|
||||||
|
} else {
|
||||||
|
CreateMergedObject -excelItem $item -csvRow ([PSCustomObject]@{Connection=$null;Status=$null; Details=$null; FailureReason=$null })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Return the merged data
|
||||||
|
return $mergedData
|
||||||
|
}
|
||||||
|
}
|
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)
|
||||||
|
}
|
33
source/Private/Update-CISExcelWorksheet.ps1
Normal file
33
source/Private/Update-CISExcelWorksheet.ps1
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
function Update-CISExcelWorksheet {
|
||||||
|
[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-WorksheetCells -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
|
||||||
|
|
||||||
|
# Save and close the Excel package
|
||||||
|
Close-ExcelPackage $excelPackage
|
||||||
|
}
|
||||||
|
}
|
28
source/Private/Update-WorksheetCells.ps1
Normal file
28
source/Private/Update-WorksheetCells.ps1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
function Update-WorksheetCells {
|
||||||
|
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)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
source/Public/Get-AdminRoleUserLicense.ps1
Normal file
86
source/Public/Get-AdminRoleUserLicense.ps1
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
<#
|
||||||
|
.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 {
|
||||||
|
[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
|
||||||
|
}
|
||||||
|
}
|
@@ -55,10 +55,10 @@
|
|||||||
- 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[]])]
|
||||||
@@ -70,28 +70,28 @@ function Invoke-M365SecurityAudit {
|
|||||||
[string]$DomainName,
|
[string]$DomainName,
|
||||||
|
|
||||||
# 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 +102,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', `
|
||||||
@@ -151,53 +151,30 @@ function Invoke-M365SecurityAudit {
|
|||||||
# Loop through each required module and assert its availability
|
# Loop through each required module and assert its availability
|
||||||
|
|
||||||
# Establishing connections
|
# 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
|
||||||
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 }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
$testDefinitions = Get-TestDefinitionsObject @params
|
||||||
# End switch ($PSCmdlet.ParameterSetName)
|
# End switch ($PSCmdlet.ParameterSetName)
|
||||||
|
# Extract unique connections needed
|
||||||
|
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
||||||
|
|
||||||
|
# Establishing connections if required
|
||||||
|
if (!($DoNotConnect)) {
|
||||||
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
||||||
|
}
|
||||||
# Determine which test files to load based on filtering
|
# Determine which test files to load based on filtering
|
||||||
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
||||||
|
|
||||||
@@ -208,7 +185,7 @@ function Invoke-M365SecurityAudit {
|
|||||||
} # 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"
|
||||||
@@ -241,18 +218,30 @@ function Invoke-M365SecurityAudit {
|
|||||||
Write-Host "Running $functionName..."
|
Write-Host "Running $functionName..."
|
||||||
$result = & $functionName @paramList
|
$result = & $functionName @paramList
|
||||||
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
||||||
$allAuditResults += $result
|
[void]($allAuditResults.add($Result))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
End {
|
End {
|
||||||
# Return all collected audit results
|
|
||||||
return $allAuditResults
|
|
||||||
# Check if the Disconnect switch is present
|
|
||||||
if (!($DoNotDisconnect)) {
|
if (!($DoNotDisconnect)) {
|
||||||
# Clean up sessions
|
# Clean up sessions
|
||||||
Disconnect-M365Suite
|
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
||||||
}
|
}
|
||||||
|
# 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%."
|
||||||
|
# Return all collected audit results
|
||||||
|
return $allAuditResults.ToArray()
|
||||||
|
# Check if the Disconnect switch is present
|
||||||
}
|
}
|
||||||
}
|
}
|
61
source/Public/Sync-CISExcelAndCsvData.ps1
Normal file
61
source/Public/Sync-CISExcelAndCsvData.ps1
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Synchronizes data between an Excel file and a CSV file and optionally updates the Excel worksheet.
|
||||||
|
.DESCRIPTION
|
||||||
|
The Sync-CISExcelAndCsvData function merges data from a specified Excel file and a CSV file based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file while preserving the original formatting and structure of the Excel worksheet.
|
||||||
|
.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.
|
||||||
|
.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.
|
||||||
|
.INPUTS
|
||||||
|
None. You cannot pipe 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 {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ExcelPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$WorksheetName,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$CsvPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[switch]$SkipUpdate
|
||||||
|
)
|
||||||
|
|
||||||
|
process {
|
||||||
|
# Merge Excel and CSV data
|
||||||
|
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
|
||||||
|
|
||||||
|
# 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
|
||||||
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
|
||||||
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
|
||||||
|
|
83
source/tests/Test-AdministrativeAccountCompliance.ps1
Normal file
83
source/tests/Test-AdministrativeAccountCompliance.ps1
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
function Test-AdministrativeAccountCompliance {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
# Aligned
|
||||||
|
# Parameters can be added if needed
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
|
||||||
|
$adminRoleUsers = @()
|
||||||
|
|
||||||
|
foreach ($role in $adminRoles) {
|
||||||
|
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
|
||||||
|
|
||||||
|
foreach ($assignment in $roleAssignments) {
|
||||||
|
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
|
||||||
|
if ($userDetails) {
|
||||||
|
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
|
||||||
|
$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|Missing: $($missingLicenses -join ',')"
|
||||||
|
}
|
||||||
|
$failureReasons = $failureReasons -join "`n"
|
||||||
|
$details = if ($nonCompliantUsers) {
|
||||||
|
"Non-Compliant Accounts: $($nonCompliantUsers.Count)`nDetails:`n" + ($nonCompliantUsers | ForEach-Object { $_.UserName }) -join "`n"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $nonCompliantUsers.Count -eq 0
|
||||||
|
$status = if ($result) { 'Pass' } else { 'Fail' }
|
||||||
|
$failureReason = if ($nonCompliantUsers) { "Non-compliant accounts: `nUsername | Roles | HybridStatus | Missing Licence`n$failureReasons" } else { "N/A" }
|
||||||
|
|
||||||
|
# Create the parameter splat
|
||||||
|
$params = @{
|
||||||
|
Rec = "1.1.1"
|
||||||
|
Result = $result
|
||||||
|
Status = $status
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReason
|
||||||
|
}
|
||||||
|
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
|
||||||
|
end {
|
||||||
|
# Output the result
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
@@ -1,16 +1,19 @@
|
|||||||
function Test-AntiPhishingPolicy {
|
function Test-AntiPhishingPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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 = @()
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
# 2.1.7 Ensure that an anti-phishing policy has been created
|
||||||
|
|
||||||
# Retrieve and validate the anti-phishing policies
|
# Retrieve and validate the anti-phishing policies
|
||||||
$antiPhishPolicies = Get-AntiPhishPolicy
|
$antiPhishPolicies = Get-AntiPhishPolicy
|
||||||
@@ -23,37 +26,54 @@ function Test-AntiPhishingPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Check if there is at least one policy that meets the requirements
|
# Check if there is at least one policy that meets the requirements
|
||||||
$isCompliant = $validatedPolicies.Count -gt 0
|
$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 details if policies are not compliant
|
# Prepare failure reasons for non-compliant items
|
||||||
$failureDetails = if (-not $isCompliant) {
|
$nonCompliantNames = $nonCompliantItems | ForEach-Object { $_.Name }
|
||||||
"No anti-phishing policy is fully compliant with CIS benchmark requirements."
|
$failureReasons = if ($nonCompliantNames.Count -gt 0) {
|
||||||
} else {
|
"Reason: Does not meet one or more compliance criteria.`nNon-compliant Policies:`n" + ($nonCompliantNames -join "`n")
|
||||||
"Compliant Anti-Phish Policy Names: " + ($validatedPolicies.Name -join ', ')
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare details for non-compliant items
|
||||||
$auditResult = [CISAuditResult]::new()
|
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
|
||||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
"Policy: $($_.Name)"
|
||||||
$auditResult.ELevel = "E5"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
$nonCompliantDetails = $nonCompliantDetails -join "`n"
|
||||||
$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
|
# 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 = "2.1.7"
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,51 @@
|
|||||||
function Test-AuditDisabledFalse {
|
function Test-AuditDisabledFalse {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
# 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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
||||||
# Pass if AuditDisabled is False. Fail otherwise.
|
|
||||||
|
# Retrieve the AuditDisabled configuration
|
||||||
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
||||||
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $auditNotDisabled) {
|
||||||
$auditResult.Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
"AuditDisabled is set to True"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "6.1.1"
|
"N/A"
|
||||||
$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
|
$details = if ($auditNotDisabled) {
|
||||||
|
"Audit is not disabled organizationally"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Audit is disabled organizationally"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.1.1"
|
||||||
|
Result = $auditNotDisabled
|
||||||
|
Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,52 @@
|
|||||||
function Test-AuditLogSearch {
|
function Test-AuditLogSearch {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
||||||
# Pass if UnifiedAuditLogIngestionEnabled is True. Fail otherwise.
|
|
||||||
|
# Retrieve the audit log configuration
|
||||||
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
||||||
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $auditLogResult) {
|
||||||
$auditResult.Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
"Audit log search is not enabled"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "3.1.1"
|
"N/A"
|
||||||
$auditResult.RecDescription = "Ensure Microsoft 365 audit log search is Enabled"
|
}
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$auditResult.CISControl = "8.2"
|
$details = if ($auditLogResult) {
|
||||||
$auditResult.CISDescription = "Collect Audit Logs"
|
"UnifiedAuditLogIngestionEnabled: True"
|
||||||
$auditResult.IG1 = $true
|
}
|
||||||
$auditResult.IG2 = $true
|
else {
|
||||||
$auditResult.IG3 = $true
|
"UnifiedAuditLogIngestionEnabled: False"
|
||||||
$auditResult.Result = $auditLogResult
|
}
|
||||||
$auditResult.Details = "UnifiedAuditLogIngestionEnabled: $($auditLogConfig.UnifiedAuditLogIngestionEnabled)"
|
|
||||||
$auditResult.FailureReason = if (-not $auditLogResult) { "Audit log search is not enabled" } else { "N/A" }
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "3.1.1"
|
||||||
|
Result = $auditLogResult
|
||||||
|
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
$auditResults += $auditResult
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,44 +1,51 @@
|
|||||||
function Test-BlockChannelEmails {
|
function Test-BlockChannelEmails {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
|
||||||
|
|
||||||
|
# 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 = "8.1.2"
|
||||||
|
Result = -not $allowEmailIntoChannel
|
||||||
|
Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,51 +1,52 @@
|
|||||||
function Test-BlockMailForwarding {
|
function Test-BlockMailForwarding {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
|
# Aligned Compare
|
||||||
# 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"
|
|
||||||
$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
|
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||||
$transportRules = Get-TransportRule | Where-Object { $_.RedirectMessageTo -ne $null }
|
|
||||||
|
# Retrieve the transport rules that redirect messages
|
||||||
|
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
||||||
$forwardingBlocked = $transportRules.Count -eq 0
|
$forwardingBlocked = $transportRules.Count -eq 0
|
||||||
|
|
||||||
$auditResult.Result = $forwardingBlocked
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.Details = if ($transportRules.Count -gt 0) {
|
$failureReasons = if ($transportRules.Count -gt 0) {
|
||||||
|
"Mail forwarding rules found: $($transportRules.Name -join ', ')"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($transportRules.Count -gt 0) {
|
||||||
$transportRules | ForEach-Object {
|
$transportRules | ForEach-Object {
|
||||||
"$($_.Name) redirects to $($_.RedirectMessageTo)"
|
"$($_.Name) redirects to $($_.RedirectMessageTo)"
|
||||||
} -join " | "
|
} -join " | "
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark."
|
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark."
|
||||||
}
|
}
|
||||||
$auditResult.FailureReason = if (-not $forwardingBlocked) {
|
|
||||||
"Mail forwarding rules found: $($transportRules.Name -join ', ')"
|
$params = @{
|
||||||
} else {
|
Rec = "6.2.1"
|
||||||
"N/A"
|
Result = $forwardingBlocked
|
||||||
|
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
}
|
}
|
||||||
$auditResult.Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return the result object
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,47 +1,53 @@
|
|||||||
function Test-BlockSharedMailboxSignIn {
|
function Test-BlockSharedMailboxSignIn {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
|
# 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.
|
|
||||||
|
|
||||||
|
# Retrieve shared mailbox details
|
||||||
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
||||||
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
||||||
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
||||||
$allBlocked = $enabledMailboxes.Count -eq 0
|
$allBlocked = $enabledMailboxes.Count -eq 0
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $allBlocked) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')"
|
||||||
$auditResult.CISControl = "0.0" # Control is explicitly not mapped
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "1.2.2"
|
"N/A"
|
||||||
$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
|
$details = if ($allBlocked) {
|
||||||
|
"All shared mailboxes have sign-in blocked."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Enabled Mailboxes: $($enabledMailboxes -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "1.2.2"
|
||||||
|
Result = $allBlocked
|
||||||
|
Status = if ($allBlocked) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,51 @@
|
|||||||
function Test-CommonAttachmentFilter {
|
function Test-CommonAttachmentFilter {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
||||||
# Pass if EnableFileFilter is set to True. Fail otherwise.
|
|
||||||
|
|
||||||
|
# Retrieve the attachment filter policy
|
||||||
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
||||||
$result = $attachmentFilter.EnableFileFilter
|
$result = $attachmentFilter.EnableFileFilter
|
||||||
$details = "File Filter Enabled: $($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 = "2.1.2"
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +1,51 @@
|
|||||||
function Test-CustomerLockbox {
|
function Test-CustomerLockbox {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 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 = "1.3.6"
|
||||||
|
Result = $customerLockboxEnabled
|
||||||
|
Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,51 @@
|
|||||||
function Test-DialInBypassLobby {
|
function Test-DialInBypassLobby {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Retrieve Teams meeting policy for PSTN users
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
|
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
|
||||||
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.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 = "8.5.4"
|
||||||
|
Result = $PSTNBypassDisabled
|
||||||
|
Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,41 +1,53 @@
|
|||||||
function Test-DisallowInfectedFilesDownload {
|
function Test-DisallowInfectedFilesDownload {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
|
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
|
||||||
|
|
||||||
|
# Retrieve the SharePoint tenant configuration
|
||||||
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
||||||
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) {
|
||||||
$auditResult.CISControl = "10.1"
|
"Downloading infected files is not disallowed."
|
||||||
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.3.1"
|
$details = if ($isDisallowInfectedFileDownloadEnabled) {
|
||||||
$auditResult.ELevel = "E5"
|
"DisallowInfectedFileDownload: True"
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $true
|
else {
|
||||||
$auditResult.IG2 = $true
|
"DisallowInfectedFileDownload: False"
|
||||||
$auditResult.IG3 = $true
|
}
|
||||||
$auditResult.RecDescription = "Ensure Office 365 SharePoint infected files are disallowed for download"
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "7.3.1"
|
||||||
|
Result = $isDisallowInfectedFileDownloadEnabled
|
||||||
|
Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
$auditResult.Result = $isDisallowInfectedFileDownloadEnabled
|
|
||||||
$auditResult.Details = "DisallowInfectedFileDownload: $($SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload)"
|
|
||||||
$auditResult.FailureReason = if (-not $isDisallowInfectedFileDownloadEnabled) { "Downloading infected files is not disallowed." } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,44 +1,52 @@
|
|||||||
function Test-EnableDKIM {
|
function Test-EnableDKIM {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 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 = "2.1.9"
|
||||||
|
Result = $dkimResult
|
||||||
|
Status = if ($dkimResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,52 @@
|
|||||||
function Test-ExternalNoControl {
|
function Test-ExternalNoControl {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 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 = "8.5.7"
|
||||||
|
Result = $externalControlRestricted
|
||||||
|
Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +1,21 @@
|
|||||||
function Test-ExternalSharingCalendars {
|
function Test-ExternalSharingCalendars {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
|
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
|
||||||
|
|
||||||
|
# Retrieve sharing policies related to calendar sharing
|
||||||
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
||||||
|
|
||||||
# Check if calendar sharing is disabled in all applicable policies
|
# Check if calendar sharing is disabled in all applicable policies
|
||||||
@@ -24,30 +28,34 @@ function Test-ExternalSharingCalendars {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $isExternalSharingDisabled) {
|
||||||
$auditResult.Rec = "1.3.3"
|
"Calendar sharing with external users is enabled in one or more policies."
|
||||||
$auditResult.RecDescription = "Ensure 'External sharing' of calendars is not available"
|
}
|
||||||
$auditResult.ELevel = "E3"
|
else {
|
||||||
$auditResult.ProfileLevel = "L2"
|
"N/A"
|
||||||
# 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
|
$details = if ($isExternalSharingDisabled) {
|
||||||
|
"Calendar sharing with external users is disabled."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "1.3.3"
|
||||||
|
Result = $isExternalSharingDisabled
|
||||||
|
Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,52 @@
|
|||||||
function Test-GlobalAdminsCount {
|
function Test-GlobalAdminsCount {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.1.3 (L1) Ensure that between two and four global admins are designated
|
# 1.1.3 (L1) Ensure that between two and four global admins are designated
|
||||||
# Pass if the count of global admins is between 2 and 4. Fail otherwise.
|
|
||||||
|
|
||||||
|
# Retrieve global admin role and members
|
||||||
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
|
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
|
||||||
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
||||||
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
|
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
|
||||||
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
|
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -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 = "1.1.3"
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,41 +1,47 @@
|
|||||||
function Test-GuestAccessExpiration {
|
function Test-GuestAccessExpiration {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 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 = "7.2.9"
|
||||||
$auditResult.Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
Result = $isGuestAccessExpirationConfiguredCorrectly
|
||||||
|
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
54
source/tests/Test-GuestUsersBiweeklyReview.ps1
Normal file
54
source/tests/Test-GuestUsersBiweeklyReview.ps1
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
function Test-GuestUsersBiweeklyReview {
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
# Aligned
|
||||||
|
# Define your parameters here if needed
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
|
# Initialization code, if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
# 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 = "1.1.4"
|
||||||
|
Result = -not $guestUsers
|
||||||
|
Status = if ($guestUsers) { "Fail" } else { "Pass" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
|
||||||
|
end {
|
||||||
|
# Return the audit result
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
@@ -1,44 +1,47 @@
|
|||||||
function Test-IdentifyExternalEmail {
|
function Test-IdentifyExternalEmail {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.3 (L1) Ensure email from external senders is identified
|
# 6.2.3 (L1) Ensure email from external senders is identified
|
||||||
# Requirement is to have external sender tagging enabled
|
|
||||||
|
|
||||||
|
# Retrieve external sender tagging configuration
|
||||||
$externalInOutlook = Get-ExternalInOutlook
|
$externalInOutlook = Get-ExternalInOutlook
|
||||||
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $externalTaggingEnabled) {
|
||||||
$auditResult.Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
"External sender tagging is disabled"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "6.2.3"
|
"N/A"
|
||||||
$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
|
$details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.2.3"
|
||||||
|
Result = $externalTaggingEnabled
|
||||||
|
Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,47 @@
|
|||||||
function Test-LinkSharingRestrictions {
|
function Test-LinkSharingRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
|
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
|
||||||
|
|
||||||
|
# Retrieve link sharing configuration for SharePoint and OneDrive
|
||||||
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
||||||
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isLinkSharingRestricted) {
|
||||||
$auditResult.CISControl = "3.3"
|
"Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.7"
|
$details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure link sharing is restricted in SharePoint and OneDrive"
|
|
||||||
|
|
||||||
$auditResult.Result = $isLinkSharingRestricted
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
$params = @{
|
||||||
$auditResult.FailureReason = if (-not $isLinkSharingRestricted) { "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)" } else { "N/A" }
|
Rec = "7.2.7"
|
||||||
$auditResult.Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
Result = $isLinkSharingRestricted
|
||||||
|
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,54 @@
|
|||||||
function Test-MailTipsEnabled {
|
function Test-MailTipsEnabled {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
||||||
|
|
||||||
|
# Retrieve organization configuration for MailTips settings
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
||||||
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
||||||
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
||||||
|
|
||||||
# Since there is no direct CIS Control mapping, the control will be set as not applicable.
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControl = "0"
|
$failureReasons = if (-not ($allTipsEnabled -and $externalRecipientsTipsEnabled)) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"One or more MailTips settings are not configured as required."
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "6.5.2"
|
$details = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) {
|
||||||
$auditResult.ELevel = "E3"
|
"MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $false
|
else {
|
||||||
$auditResult.IG2 = $false
|
"One or more MailTips settings are not configured as required."
|
||||||
$auditResult.IG3 = $false
|
}
|
||||||
$auditResult.RecDescription = "Ensure MailTips are enabled for end users"
|
|
||||||
|
|
||||||
$auditResult.Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
$params = @{
|
||||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "One or more MailTips settings are not configured as required." } else { "N/A" }
|
Rec = "6.5.2"
|
||||||
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
|
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
||||||
|
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,26 +1,20 @@
|
|||||||
function Test-MailboxAuditingE3 {
|
function Test-MailboxAuditingE3 {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
|
# Create Table for Details
|
||||||
# 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
|
||||||
@@ -72,10 +66,19 @@ function Test-MailboxAuditingE3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$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) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
||||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.1.2"
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,23 +1,21 @@
|
|||||||
function Test-MailboxAuditingE5 {
|
function Test-MailboxAuditingE5 {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param ()
|
param (
|
||||||
|
# Aligned
|
||||||
|
# Create Table for Details
|
||||||
|
# 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
|
||||||
@@ -31,15 +29,11 @@ function Test-MailboxAuditingE5 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
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 = @()
|
||||||
@@ -78,13 +72,19 @@ function Test-MailboxAuditingE5 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 { $allFailures -join " | " }
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.1.3"
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
}
|
}
|
||||||
$auditResult.Result = $allFailures.Count -eq 0
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$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 {
|
||||||
@@ -117,4 +117,4 @@ function Format-MissingActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return $formattedResults -join '; '
|
return $formattedResults -join '; '
|
||||||
}
|
}
|
||||||
|
@@ -1,54 +1,52 @@
|
|||||||
function Test-ManagedApprovedPublicGroups {
|
function Test-ManagedApprovedPublicGroups {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResults = @()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
|
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
|
||||||
|
|
||||||
|
# Retrieve all public groups
|
||||||
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
$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"
|
|
||||||
$auditResult.Status = "Pass"
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
||||||
$detailsString = $groupDetails -join ', '
|
"Public groups found: $($groupDetails -join ', ')"
|
||||||
|
|
||||||
$auditResult.Result = $false
|
|
||||||
$auditResult.Details = "Public groups found: $detailsString"
|
|
||||||
$auditResult.FailureReason = "There are public groups present that are not organizationally managed/approved."
|
|
||||||
$auditResult.Status = "Fail"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "1.2.1"
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResults
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,46 +1,48 @@
|
|||||||
function Test-MeetingChatNoAnonymous {
|
function Test-MeetingChatNoAnonymous {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
||||||
# Name doesn't match profile level in benchmarks either.
|
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
|
# Retrieve the Teams meeting policy for meeting chat
|
||||||
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
||||||
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
|
$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 = "8.5.5"
|
||||||
|
Result = $chatAnonDisabled
|
||||||
|
Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +1,42 @@
|
|||||||
function Test-ModernAuthExchangeOnline {
|
function Test-ModernAuthExchangeOnline {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = "6.5.1"
|
||||||
|
Result = $orgConfig.OAuth2ClientProfileEnabled
|
||||||
|
Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
@@ -43,12 +45,7 @@ function Test-ModernAuthExchangeOnline {
|
|||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
function Test-ModernAuthSharePoint {
|
function Test-ModernAuthSharePoint {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -15,25 +16,29 @@ function Test-ModernAuthSharePoint {
|
|||||||
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
||||||
$modernAuthForSPRequired = -not $SPOTenant.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 = "7.2.1"
|
||||||
$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
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
function Test-NoAnonymousMeetingJoin {
|
function Test-NoAnonymousMeetingJoin {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -18,28 +19,29 @@ function Test-NoAnonymousMeetingJoin {
|
|||||||
$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 = "8.5.1"
|
||||||
|
Result = -not $allowAnonymousUsersToJoinMeeting
|
||||||
|
Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
function Test-NoAnonymousMeetingStart {
|
function Test-NoAnonymousMeetingStart {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -18,28 +19,29 @@ function Test-NoAnonymousMeetingStart {
|
|||||||
$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 = "8.5.2"
|
||||||
|
Result = $anonymousStartDisabled
|
||||||
|
Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,47 @@
|
|||||||
function Test-NoWhitelistDomains {
|
function Test-NoWhitelistDomains {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
||||||
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $_.SenderDomainIs -ne $null }
|
|
||||||
|
|
||||||
$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
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.RecDescription = "Ensure mail transport rules do not whitelist specific domains"
|
|
||||||
|
|
||||||
if ($whitelistedRules) {
|
|
||||||
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
|
|
||||||
$auditResult.Result = $false
|
|
||||||
$auditResult.Details = "Whitelisted Rules: $($ruleDetails -join '; ')"
|
|
||||||
$auditResult.FailureReason = "There are transport rules whitelisting specific domains."
|
|
||||||
$auditResult.Status = "Fail"
|
|
||||||
} else {
|
|
||||||
$auditResult.Result = $true
|
|
||||||
$auditResult.Details = "No transport rules whitelisting specific domains found."
|
|
||||||
$auditResult.FailureReason = "N/A"
|
|
||||||
$auditResult.Status = "Pass"
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($whitelistedRules) {
|
||||||
|
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
|
||||||
|
"Whitelisted Rules: $($ruleDetails -join '; ')"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"No transport rules whitelisting specific domains found."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.2.2"
|
||||||
|
Result = -not $whitelistedRules
|
||||||
|
Status = if ($whitelistedRules) { "Fail" } else { "Pass" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,15 +1,19 @@
|
|||||||
function Test-NotifyMalwareInternal {
|
function Test-NotifyMalwareInternal {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
||||||
|
|
||||||
# Retrieve all 'Custom' malware filter policies and check notification settings
|
# Retrieve all 'Custom' malware filter policies and check notification settings
|
||||||
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
||||||
$policiesToReport = @()
|
$policiesToReport = @()
|
||||||
@@ -22,31 +26,35 @@ function Test-NotifyMalwareInternal {
|
|||||||
|
|
||||||
# Determine the result based on the presence of custom policies without notifications
|
# Determine the result based on the presence of custom policies without notifications
|
||||||
$result = $policiesToReport.Count -eq 0
|
$result = $policiesToReport.Count -eq 0
|
||||||
$details = if ($result) { "All custom malware policies have notifications enabled." } else { "Misconfigured Policies: $($policiesToReport -join ', ')" }
|
|
||||||
$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
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($result) {
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "2.1.3"
|
"Some custom policies do not have notifications for internal users sending malware enabled."
|
||||||
$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
|
$details = if ($result) {
|
||||||
|
"All custom malware policies have notifications enabled."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Misconfigured Policies: $($policiesToReport -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "2.1.3"
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,47 @@
|
|||||||
function Test-OneDriveContentRestrictions {
|
function Test-OneDriveContentRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||||
|
|
||||||
|
# Retrieve OneDrive sharing capability settings
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
||||||
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isOneDriveSharingRestricted) {
|
||||||
$auditResult.CISControl = "3.3"
|
"OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)"
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.4"
|
$details = if ($isOneDriveSharingRestricted) {
|
||||||
$auditResult.ELevel = "E3"
|
"OneDrive content sharing is restricted."
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $true
|
else {
|
||||||
$auditResult.IG2 = $true
|
"OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
||||||
$auditResult.IG3 = $true
|
}
|
||||||
$auditResult.RecDescription = "Ensure OneDrive content sharing is restricted"
|
|
||||||
|
|
||||||
$auditResult.Result = $isOneDriveSharingRestricted
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
$params = @{
|
||||||
$auditResult.FailureReason = if (-not $isOneDriveSharingRestricted) { "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)" } else { "N/A" }
|
Rec = "7.2.4"
|
||||||
$auditResult.Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
Result = $isOneDriveSharingRestricted
|
||||||
|
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,37 +1,47 @@
|
|||||||
function Test-OneDriveSyncRestrictions {
|
function Test-OneDriveSyncRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
|
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
|
||||||
|
|
||||||
|
# Retrieve OneDrive sync client restriction settings
|
||||||
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
||||||
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isSyncRestricted) {
|
||||||
$auditResult.CISControl = "0.0"
|
"OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs."
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.3.2"
|
$details = if ($isSyncRestricted) {
|
||||||
$auditResult.ELevel = "E3"
|
"OneDrive sync is restricted for unmanaged devices."
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $false
|
else {
|
||||||
$auditResult.IG2 = $false
|
"TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
||||||
$auditResult.IG3 = $false
|
}
|
||||||
$auditResult.RecDescription = "Ensure OneDrive sync is restricted for unmanaged devices"
|
|
||||||
|
|
||||||
$auditResult.Result = $isSyncRestricted
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
$params = @{
|
||||||
$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" }
|
Rec = "7.3.2"
|
||||||
$auditResult.Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
Result = $isSyncRestricted
|
||||||
|
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
function Test-OrgOnlyBypassLobby {
|
function Test-OrgOnlyBypassLobby {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -15,31 +16,38 @@ function Test-OrgOnlyBypassLobby {
|
|||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
|
# Retrieve the Teams meeting policy for lobby bypass settings
|
||||||
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
|
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
|
||||||
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
|
$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 = "8.5.3"
|
||||||
|
Result = $lobbyBypassRestricted
|
||||||
|
Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
function Test-OrganizersPresent {
|
function Test-OrganizersPresent {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -15,31 +16,38 @@ function Test-OrganizersPresent {
|
|||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
|
# Retrieve the Teams meeting policy for presenters
|
||||||
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
|
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
|
||||||
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
|
$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 = "8.5.6"
|
||||||
|
Result = $presenterRoleRestricted
|
||||||
|
Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,43 +1,47 @@
|
|||||||
function Test-PasswordHashSync {
|
function Test-PasswordHashSync {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
||||||
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
|
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
|
||||||
$passwordHashSync = Get-MgOrganization | Select-Object OnPremisesSyncEnabled
|
|
||||||
$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 = "5.1.8.1"
|
||||||
|
Result = $hashSyncResult
|
||||||
|
Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,45 +1,47 @@
|
|||||||
function Test-PasswordNeverExpirePolicy {
|
function Test-PasswordNeverExpirePolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string]$DomainName # DomainName parameter is now mandatory
|
[string]$DomainName # DomainName parameter is now mandatory
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script
|
# Dot source the class script if necessary
|
||||||
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
$auditResults = @()
|
# Initialization code, if needed
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
|
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
|
||||||
# Pass if PasswordValidityPeriodInDays is 0.
|
# Pass if PasswordValidityPeriodInDays is 0. Fail otherwise.
|
||||||
# Fail otherwise.
|
|
||||||
|
|
||||||
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object PasswordValidityPeriodInDays
|
# Retrieve password expiration policy
|
||||||
|
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object -ExpandProperty PasswordValidityPeriodInDays
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($passwordPolicy -ne 0) {
|
||||||
$auditResult.Rec = "1.3.1"
|
"Password expiration is not set to never expire"
|
||||||
$auditResult.RecDescription = "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'"
|
}
|
||||||
$auditResult.ELevel = "E3"
|
else {
|
||||||
$auditResult.ProfileLevel = "L1"
|
"N/A"
|
||||||
$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
|
$details = "Validity Period: $passwordPolicy days"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "1.3.1"
|
||||||
|
Result = $passwordPolicy -eq 0
|
||||||
|
Status = if ($passwordPolicy -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,42 @@
|
|||||||
function Test-ReauthWithCode {
|
function Test-ReauthWithCode {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||||
|
|
||||||
|
# Retrieve reauthentication settings for SharePoint Online
|
||||||
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
||||||
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isReauthenticationRestricted) {
|
||||||
$auditResult.CISControl = "0.0"
|
"Reauthentication with verification code does not require reauthentication within 15 days or less."
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.10"
|
$details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false
|
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.RecDescription = "Ensure reauthentication with verification code is restricted"
|
|
||||||
|
|
||||||
$auditResult.Result = $isReauthenticationRestricted
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
$params = @{
|
||||||
$auditResult.FailureReason = if (-not $isReauthenticationRestricted) { "Reauthentication with verification code does not require reauthentication within 15 days or less." } else { "N/A" }
|
Rec = "7.2.10"
|
||||||
$auditResult.Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
Result = $isReauthenticationRestricted
|
||||||
|
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,21 +1,20 @@
|
|||||||
function Test-ReportSecurityInTeams {
|
function Test-ReportSecurityInTeams {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.6.1 (L1) Ensure users can report security concerns in Teams
|
# 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
|
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
|
||||||
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
||||||
|
|
||||||
@@ -25,32 +24,33 @@ function Test-ReportSecurityInTeams {
|
|||||||
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
||||||
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $securityReportEnabled) {
|
||||||
$auditResult.CISControlVer = "v8"
|
"Users cannot report security concerns in Teams due to one or more incorrect settings"
|
||||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
}
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
else {
|
||||||
$auditResult.Rec = "8.6.1"
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
$details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
|
||||||
$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); " +
|
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
|
||||||
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
|
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
|
||||||
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
|
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
|
||||||
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
|
"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 = "8.6.1"
|
||||||
|
Result = $securityReportEnabled
|
||||||
|
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,21 +1,20 @@
|
|||||||
function Test-RestrictCustomScripts {
|
function Test-RestrictCustomScripts {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
|
# Aligned
|
||||||
# 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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# CIS 2.7 Ensure custom script execution is restricted on site collections
|
# 7.3.4 (L1) 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
|
# Retrieve all site collections and select necessary properties
|
||||||
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
||||||
|
|
||||||
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
|
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
|
||||||
@@ -29,42 +28,34 @@ function Test-RestrictCustomScripts {
|
|||||||
"$($_.Title) ($($_.Url)): Custom Script Allowed"
|
"$($_.Title) ($($_.Url)): Custom Script Allowed"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if (-not $complianceResult) {
|
||||||
$auditResult.CISControlVer = "v8"
|
|
||||||
$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 "; ")
|
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
$auditResult.Status = if ($complianceResult) {
|
|
||||||
"Pass"
|
$details = if ($complianceResult) {
|
||||||
} else {
|
"All site collections have custom script execution restricted"
|
||||||
"Fail"
|
}
|
||||||
|
else {
|
||||||
|
$nonCompliantSiteDetails -join "; "
|
||||||
}
|
}
|
||||||
|
|
||||||
$auditResults += $auditResult
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "7.3.4"
|
||||||
|
Result = $complianceResult
|
||||||
|
Status = if ($complianceResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,37 +1,42 @@
|
|||||||
function Test-RestrictExternalSharing {
|
function Test-RestrictExternalSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.3 (L1) Ensure external content sharing is restricted
|
# 7.2.3 (L1) Ensure external content sharing is restricted
|
||||||
|
|
||||||
|
# Retrieve the SharingCapability setting for the SharePoint tenant
|
||||||
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
||||||
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult.CISControlVer = "v8"
|
$failureReasons = if (-not $isRestricted) {
|
||||||
$auditResult.CISControl = "3.3"
|
"External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)"
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$auditResult.Rec = "7.2.3"
|
$details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
||||||
$auditResult.ELevel = "E3"
|
|
||||||
$auditResult.ProfileLevel = "L1"
|
|
||||||
$auditResult.IG1 = $true
|
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure external content sharing is restricted"
|
|
||||||
|
|
||||||
$auditResult.Result = $isRestricted
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult.Details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
$params = @{
|
||||||
$auditResult.FailureReason = if (-not $isRestricted) { "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)" } else { "N/A" }
|
Rec = "7.2.3"
|
||||||
$auditResult.Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
Result = $isRestricted
|
||||||
|
Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,20 +1,20 @@
|
|||||||
function Test-RestrictOutlookAddins {
|
function Test-RestrictOutlookAddins {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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')
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Main functionality
|
|
||||||
# 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
|
||||||
@@ -38,24 +38,11 @@ function Test-RestrictOutlookAddins {
|
|||||||
if ($defaultPolicyRoles) {
|
if ($defaultPolicyRoles) {
|
||||||
$defaultPolicyFailureDetails = $defaultPolicyRoles
|
$defaultPolicyFailureDetails = $defaultPolicyRoles
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
end {
|
|
||||||
# Prepare result object
|
|
||||||
$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"
|
|
||||||
|
|
||||||
|
# Prepare result details string
|
||||||
$detailsString = ""
|
$detailsString = ""
|
||||||
if ($customPolicyFailures) {
|
if ($customPolicyFailures) {
|
||||||
$detailsString += "Custom Policy Failures: | "
|
$detailsString += "Custom Policy Failures: | "
|
||||||
# Use pipes or tabs here instead of newlines
|
|
||||||
$detailsString += ($customPolicyFailures -join " | ")
|
$detailsString += ($customPolicyFailures -join " | ")
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -70,20 +57,22 @@ function Test-RestrictOutlookAddins {
|
|||||||
$detailsString += "Compliant"
|
$detailsString += "Compliant"
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($customPolicyFailures -or $defaultPolicyFailureDetails) {
|
# Determine result based on findings
|
||||||
$auditResult.Result = $false
|
$isCompliant = -not ($customPolicyFailures -or $defaultPolicyFailureDetails)
|
||||||
$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
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.3.1"
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
end {
|
||||||
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,48 +1,56 @@
|
|||||||
function Test-RestrictStorageProvidersOutlook {
|
function Test-RestrictStorageProvidersOutlook {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
||||||
$owaPolicies = Get-OwaMailboxPolicy
|
|
||||||
$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
|
|
||||||
$auditResult.Details = if($allPoliciesRestricted) {
|
|
||||||
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
|
|
||||||
} else {
|
|
||||||
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable } | Select-Object -ExpandProperty Name
|
|
||||||
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies -join ', ')"
|
|
||||||
}
|
}
|
||||||
$auditResult.FailureReason = if(-not $allPoliciesRestricted) { "One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable." } else { "N/A" }
|
else {
|
||||||
$auditResult.Status = if($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
"One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable."
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($allPoliciesRestricted) {
|
||||||
|
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies.Name -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "6.5.3"
|
||||||
|
Result = $allPoliciesRestricted
|
||||||
|
Status = if ($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
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,48 @@
|
|||||||
function Test-RestrictTenantCreation {
|
function Test-RestrictTenantCreation {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
||||||
# Pass if AllowedToCreateTenants is False. Fail otherwise.
|
|
||||||
|
# Retrieve the tenant creation policy
|
||||||
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
|
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
|
||||||
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Prepare failure reasons and details based on compliance
|
||||||
$auditResult = [CISAuditResult]::new()
|
$failureReasons = if ($tenantCreationResult) {
|
||||||
$auditResult.Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
"N/A"
|
||||||
$auditResult.ELevel = "E3"
|
}
|
||||||
$auditResult.ProfileLevel = "L1"
|
else {
|
||||||
$auditResult.Rec = "5.1.2.3"
|
"Non-admin users can create tenants"
|
||||||
$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
|
$details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "5.1.2.3"
|
||||||
|
Result = $tenantCreationResult
|
||||||
|
Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional helper functions (if any)
|
||||||
|
@@ -1,49 +1,53 @@
|
|||||||
function Test-SafeAttachmentsPolicy {
|
function Test-SafeAttachmentsPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
# 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
||||||
|
|
||||||
# Retrieve all Safe Attachment policies where Enable is set to True
|
# Retrieve all Safe Attachment policies where Enable is set to True
|
||||||
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
|
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
|
||||||
|
|
||||||
# If there are any enabled policies, the result is Pass. If not, it's Fail.
|
# Determine result and details based on the presence of enabled policies
|
||||||
$result = $safeAttachmentPolicies -ne $null -and $safeAttachmentPolicies.Count -gt 0
|
$result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
|
||||||
$details = if ($result) {
|
$details = if ($result) {
|
||||||
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
"No Safe Attachments Policies are enabled."
|
"No Safe Attachments Policies are enabled."
|
||||||
}
|
}
|
||||||
$failureReason = if ($result) { "N/A" } else { "Safe Attachments policy is not enabled." }
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
$failureReasons = if ($result) {
|
||||||
$auditResult = [CISAuditResult]::new()
|
"N/A"
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
}
|
||||||
$auditResult.ELevel = "E5"
|
else {
|
||||||
$auditResult.ProfileLevel = "L2"
|
"Safe Attachments policy is not enabled."
|
||||||
$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
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "2.1.4"
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional helper functions (if any)
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
function Test-SafeAttachmentsTeams {
|
function Test-SafeAttachmentsTeams {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Requires E5 license
|
# 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
|
||||||
|
|
||||||
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
|
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
|
||||||
$atpPolicies = Get-AtpPolicyForO365
|
$atpPolicies = Get-AtpPolicyForO365
|
||||||
|
|
||||||
@@ -25,33 +28,33 @@ function Test-SafeAttachmentsTeams {
|
|||||||
$result = $null -ne $atpPolicyResult
|
$result = $null -ne $atpPolicyResult
|
||||||
$details = if ($result) {
|
$details = if ($result) {
|
||||||
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
||||||
}
|
}
|
||||||
$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
|
$failureReasons = if ($result) {
|
||||||
$auditResult = [CISAuditResult]::new()
|
"N/A"
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
}
|
||||||
$auditResult.ELevel = "E5"
|
else {
|
||||||
$auditResult.ProfileLevel = "L2"
|
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
||||||
$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
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = "2.1.5"
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Additional helper functions (if any)
|
||||||
|
@@ -1,16 +1,19 @@
|
|||||||
function Test-SafeLinksOfficeApps {
|
function Test-SafeLinksOfficeApps {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||||
|
|
||||||
# Retrieve all Safe Links policies
|
# Retrieve all Safe Links policies
|
||||||
$policies = Get-SafeLinksPolicy
|
$policies = Get-SafeLinksPolicy
|
||||||
|
|
||||||
@@ -42,29 +45,21 @@ function Test-SafeLinksOfficeApps {
|
|||||||
# Prepare the final result
|
# Prepare the final result
|
||||||
$result = $misconfiguredDetails.Count -eq 0
|
$result = $misconfiguredDetails.Count -eq 0
|
||||||
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
$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 the audit result object
|
# Create and populate the CISAuditResult object
|
||||||
$auditResult = [CISAuditResult]::new()
|
$params = @{
|
||||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
Rec = "2.1.1"
|
||||||
$auditResult.ELevel = "E5"
|
Result = $result
|
||||||
$auditResult.ProfileLevel = "L2"
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "2.1.1"
|
Details = $details
|
||||||
$auditResult.RecDescription = "Ensure Safe Links for Office Applications is Enabled"
|
FailureReason = $failureReasons
|
||||||
$auditResult.CISControlVer = "v8"
|
}
|
||||||
$auditResult.CISControl = "10.1"
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
function Test-SharePointAADB2B {
|
function Test-SharePointAADB2B {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
@@ -15,22 +18,14 @@ function Test-SharePointAADB2B {
|
|||||||
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
$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 = "7.2.2"
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
||||||
|
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "7.2.2"
|
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
||||||
$auditResult.ELevel = "E3"
|
FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
||||||
$auditResult.ProfileLevel = "L1"
|
}
|
||||||
$auditResult.IG1 = $false
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$auditResult.IG2 = $false
|
|
||||||
$auditResult.IG3 = $false
|
|
||||||
$auditResult.RecDescription = "Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled"
|
|
||||||
|
|
||||||
$auditResult.Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
|
||||||
$auditResult.Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
|
||||||
$auditResult.FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
function Test-SharePointExternalSharingDomains {
|
function Test-SharePointExternalSharingDomains {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
@@ -16,22 +19,14 @@ function Test-SharePointExternalSharingDomains {
|
|||||||
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
|
$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 = "7.2.6"
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
Result = $isDomainRestrictionConfigured
|
||||||
|
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "7.2.6"
|
Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
|
||||||
$auditResult.ELevel = "E3"
|
FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $true
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure SharePoint external sharing is managed through domain whitelist/blacklists"
|
|
||||||
|
|
||||||
$auditResult.Result = $isDomainRestrictionConfigured
|
|
||||||
$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" }
|
|
||||||
$auditResult.Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,11 +1,14 @@
|
|||||||
function Test-SharePointGuestsItemSharing {
|
function Test-SharePointGuestsItemSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
@@ -16,22 +19,14 @@ function Test-SharePointGuestsItemSharing {
|
|||||||
$isGuestResharingPrevented = $SPOTenant.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 = "7.2.5"
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
Result = $isGuestResharingPrevented
|
||||||
|
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "7.2.5"
|
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
||||||
$auditResult.ELevel = "E3"
|
FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $true
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$auditResult.IG2 = $true
|
|
||||||
$auditResult.IG3 = $true
|
|
||||||
$auditResult.RecDescription = "Ensure that SharePoint guest users cannot share items they don't own"
|
|
||||||
|
|
||||||
$auditResult.Result = $isGuestResharingPrevented
|
|
||||||
$auditResult.Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
|
||||||
$auditResult.FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
|
||||||
$auditResult.Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,15 +1,21 @@
|
|||||||
function Test-SpamPolicyAdminNotify {
|
function Test-SpamPolicyAdminNotify {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
|
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
|
||||||
|
|
||||||
# Get the default hosted outbound spam filter policy
|
# Get the default hosted outbound spam filter policy
|
||||||
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
||||||
|
|
||||||
@@ -28,29 +34,18 @@ function Test-SpamPolicyAdminNotify {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Create an instance of CISAuditResult and populate it
|
||||||
$auditResult = [CISAuditResult]::new()
|
$params = @{
|
||||||
$auditResult.Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
Rec = "2.1.6"
|
||||||
$auditResult.ELevel = "E3"
|
Result = $areSettingsEnabled
|
||||||
$auditResult.ProfileLevel = "L1"
|
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "2.1.6"
|
Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
|
||||||
$auditResult.RecDescription = "Ensure Exchange Online Spam Policies are set to notify administrators"
|
FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
|
||||||
$auditResult.CISControlVer = "v8"
|
}
|
||||||
$auditResult.CISControl = "17.5"
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$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,13 +1,16 @@
|
|||||||
function Test-TeamsExternalAccess {
|
function Test-TeamsExternalAccess {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -26,27 +29,18 @@ function Test-TeamsExternalAccess {
|
|||||||
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Create an instance of CISAuditResult and populate it
|
||||||
$auditResult = [CISAuditResult]::new()
|
$params = @{
|
||||||
$auditResult.CISControlVer = "v8"
|
Rec = "8.2.1"
|
||||||
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
|
Result = $isCompliant
|
||||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "8.2.1"
|
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
|
||||||
$auditResult.ELevel = "E3"
|
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,16 @@
|
|||||||
function Test-TeamsExternalFileSharing {
|
function Test-TeamsExternalFileSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -30,27 +33,18 @@ function Test-TeamsExternalFileSharing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Create an instance of CISAuditResult and populate it
|
||||||
$auditResult = [CISAuditResult]::new()
|
$params = @{
|
||||||
$auditResult.CISControlVer = "v8"
|
Rec = "8.1.1"
|
||||||
$auditResult.CISControl = "3.3"
|
Result = $isCompliant
|
||||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
$auditResult.Rec = "8.1.1"
|
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
||||||
$auditResult.ELevel = "E3"
|
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
||||||
$auditResult.ProfileLevel = "L2"
|
}
|
||||||
$auditResult.IG1 = $true # Set based on the benchmark
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
$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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return auditResult
|
||||||
return $auditResults
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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/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/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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Write-AuditLog.tests.ps1
Normal file
27
tests/Unit/Private/Write-AuditLog.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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -1,91 +0,0 @@
|
|||||||
BeforeAll {
|
|
||||||
$script:dscModuleName = 'M365FoundationsCISReport'
|
|
||||||
|
|
||||||
Import-Module -Name $script:dscModuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
AfterAll {
|
|
||||||
# Unload the module being tested so that it doesn't impact any other tests.
|
|
||||||
Get-Module -Name $script:dscModuleName -All | Remove-Module -Force
|
|
||||||
}
|
|
||||||
|
|
||||||
Describe Get-Something {
|
|
||||||
BeforeAll {
|
|
||||||
Mock -CommandName Get-PrivateFunction -MockWith {
|
|
||||||
# This return the value passed to the Get-PrivateFunction parameter $PrivateData.
|
|
||||||
$PrivateData
|
|
||||||
} -ModuleName $dscModuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
Context 'When passing values using named parameters' {
|
|
||||||
It 'Should call the private function once' {
|
|
||||||
{ Get-Something -Data 'value' } | Should -Not -Throw
|
|
||||||
|
|
||||||
Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 1 -Scope It -ModuleName $dscModuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should return a single object' {
|
|
||||||
$return = Get-Something -Data 'value'
|
|
||||||
|
|
||||||
($return | Measure-Object).Count | Should -Be 1
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should return the correct string value' {
|
|
||||||
$return = Get-Something -Data 'value'
|
|
||||||
|
|
||||||
$return | Should -Be 'value'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Context 'When passing values over the pipeline' {
|
|
||||||
It 'Should call the private function two times' {
|
|
||||||
{ 'value1', 'value2' | Get-Something } | Should -Not -Throw
|
|
||||||
|
|
||||||
Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 2 -Scope It -ModuleName $dscModuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should return an array with two items' {
|
|
||||||
$return = 'value1', 'value2' | Get-Something
|
|
||||||
|
|
||||||
$return.Count | Should -Be 2
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should return an array with the correct string values' {
|
|
||||||
$return = 'value1', 'value2' | Get-Something
|
|
||||||
|
|
||||||
$return[0] | Should -Be 'value1'
|
|
||||||
$return[1] | Should -Be 'value2'
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should accept values from the pipeline by property name' {
|
|
||||||
$return = 'value1', 'value2' | ForEach-Object {
|
|
||||||
[PSCustomObject]@{
|
|
||||||
Data = $_
|
|
||||||
OtherProperty = 'other'
|
|
||||||
}
|
|
||||||
} | Get-Something
|
|
||||||
|
|
||||||
$return[0] | Should -Be 'value1'
|
|
||||||
$return[1] | Should -Be 'value2'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Context 'When passing WhatIf' {
|
|
||||||
It 'Should support the parameter WhatIf' {
|
|
||||||
(Get-Command -Name 'Get-Something').Parameters.ContainsKey('WhatIf') | Should -Be $true
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should not call the private function' {
|
|
||||||
{ Get-Something -Data 'value' -WhatIf } | Should -Not -Throw
|
|
||||||
|
|
||||||
Should -Invoke -CommandName Get-PrivateFunction -Exactly -Times 0 -Scope It -ModuleName $dscModuleName
|
|
||||||
}
|
|
||||||
|
|
||||||
It 'Should return $null' {
|
|
||||||
$return = Get-Something -Data 'value' -WhatIf
|
|
||||||
|
|
||||||
$return | Should -BeNullOrEmpty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Reference in New Issue
Block a user