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
|
||||
node_modules
|
||||
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
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
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).
|
||||
|
||||
[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
|
||||
### Synopsis
|
||||
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> [-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>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 |
|
||||
@@ -55,10 +88,10 @@ Invoke-M365SecurityAudit -TenantAdminUrl <String> -DomainName <String> [-SkipRec
|
||||
- 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.
|
||||
- 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
|
||||
- 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
|
||||
**EXAMPLE 1**
|
||||
@@ -88,4 +121,45 @@ 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])
|
||||
- [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>
|
||||
<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>
|
||||
<description>
|
||||
If you'd like to regenerate the documentation, please open up powershell and run
|
||||
@@ -97,13 +97,95 @@
|
||||
</div>
|
||||
|
||||
<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="#Sync-CISExcelAndCsvData">Sync-CISExcelAndCsvData</a></li>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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 class="page-header">
|
||||
<h2> Invoke-M365SecurityAudit </h2>
|
||||
@@ -274,8 +356,12 @@ The cmdlet returns an array of CISAuditResult objects representing the results o
|
||||
<div class='panel-heading'>
|
||||
<h3 class='panel-title'> Note </h3>
|
||||
</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:
|
||||
https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en</div>
|
||||
<div class='panel-body'>- 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</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3> Examples </h3>
|
||||
@@ -302,7 +388,114 @@ PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation</p
|
||||
<h3> Links </h3>
|
||||
<div>
|
||||
<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>
|
||||
</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
|
||||
.\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"
|
||||
|
||||
|
||||
<#
|
||||
$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]$ELevel
|
||||
[string]$ProfileLevel
|
||||
[bool]$Automated
|
||||
[string]$Connection
|
||||
[string]$Rec
|
||||
[string]$RecDescription
|
||||
[string]$CISControlVer = 'v8'
|
||||
|
@@ -102,10 +102,10 @@ PrivateData = @{
|
||||
LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en'
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
ProjectUri = 'https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport'
|
||||
|
||||
# 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 = ''
|
||||
|
@@ -1,56 +1,58 @@
|
||||
function Connect-M365Suite {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Parameter to specify the SharePoint Online Tenant Admin URL
|
||||
[Parameter(Mandatory)]
|
||||
[string]$TenantAdminUrl
|
||||
)
|
||||
$VerbosePreference = "SilentlyContinue"
|
||||
try {
|
||||
[string]$TenantAdminUrl,
|
||||
|
||||
# Attempt to connect to Azure Active Directory
|
||||
[Parameter(Mandatory)]
|
||||
[string[]]$RequiredConnections
|
||||
)
|
||||
|
||||
$VerbosePreference = "SilentlyContinue"
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
|
||||
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
|
||||
Connect-AzureAD | Out-Null
|
||||
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Attempt to connect to Exchange Online
|
||||
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
||||
Connect-ExchangeOnline | Out-Null
|
||||
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
||||
try {
|
||||
# Attempt to connect to Microsoft Graph with specified scopes
|
||||
if ($RequiredConnections -contains "Microsoft Graph") {
|
||||
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
||||
try {
|
||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||
}
|
||||
catch {
|
||||
Write-Host "Failed to connect o MgGraph, attempting device auth." -ForegroundColor Yellow
|
||||
# Attempt to connect to Microsoft Graph with specified scopes
|
||||
Write-Host "Connecting to Microsoft Graph using device auth with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
||||
Write-Host "Failed to connect to MgGraph, attempting device auth." -ForegroundColor Yellow
|
||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
|
||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Validate SharePoint Online Tenant Admin URL
|
||||
if (-not $TenantAdminUrl) {
|
||||
throw "SharePoint Online Tenant Admin URL is required."
|
||||
}
|
||||
|
||||
# Attempt to connect to SharePoint Online
|
||||
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
||||
Connect-ExchangeOnline | Out-Null
|
||||
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
||||
}
|
||||
|
||||
if ($RequiredConnections -contains "SPO") {
|
||||
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
|
||||
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
||||
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
|
||||
}
|
||||
|
||||
# Attempt to connect to Microsoft Teams
|
||||
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
|
||||
Connect-MicrosoftTeams | Out-Null
|
||||
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
catch {
|
||||
$VerbosePreference = "Continue"
|
||||
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
|
||||
throw $_
|
||||
}
|
||||
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
|
@@ -1,39 +1,59 @@
|
||||
function Disconnect-M365Suite {
|
||||
param (
|
||||
[Parameter(Mandatory)]
|
||||
[string[]]$RequiredConnections
|
||||
)
|
||||
|
||||
# Clean up sessions
|
||||
try {
|
||||
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
|
||||
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disconnect from Exchange Online: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
|
||||
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
|
||||
Disconnect-AzureAD | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disconnect from Azure AD: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "Microsoft Graph") {
|
||||
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
||||
Disconnect-MgGraph | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "SPO") {
|
||||
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
|
||||
Disconnect-SPOService | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disconnect from SharePoint Online: $_"
|
||||
}
|
||||
|
||||
try {
|
||||
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
|
||||
Disconnect-MicrosoftTeams | Out-Null
|
||||
}
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
|
||||
}
|
||||
Write-Host "All sessions have been disconnected." -ForegroundColor Green
|
||||
|
||||
Write-Host "All necessary sessions have been disconnected." -ForegroundColor Green
|
||||
}
|
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.
|
||||
- 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
|
||||
.LINK
|
||||
Online Version: [GitHub Repository URL]
|
||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||
#>
|
||||
|
||||
function Invoke-M365SecurityAudit {
|
||||
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
||||
[OutputType([CISAuditResult[]])]
|
||||
@@ -70,28 +70,28 @@ function Invoke-M365SecurityAudit {
|
||||
[string]$DomainName,
|
||||
|
||||
# E-Level with optional ProfileLevel selection
|
||||
[Parameter(ParameterSetName = 'ELevelFilter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
|
||||
[ValidateSet('E3', 'E5')]
|
||||
[string]$ELevel,
|
||||
|
||||
[Parameter(ParameterSetName = 'ELevelFilter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
|
||||
[ValidateSet('L1', 'L2')]
|
||||
[string]$ProfileLevel,
|
||||
|
||||
# IG Filters, one at a time
|
||||
[Parameter(ParameterSetName = 'IG1Filter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')]
|
||||
[switch]$IncludeIG1,
|
||||
|
||||
[Parameter(ParameterSetName = 'IG2Filter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')]
|
||||
[switch]$IncludeIG2,
|
||||
|
||||
[Parameter(ParameterSetName = 'IG3Filter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')]
|
||||
[switch]$IncludeIG3,
|
||||
|
||||
# Inclusion of specific recommendation numbers
|
||||
[Parameter(ParameterSetName = 'RecFilter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
||||
[ValidateSet(
|
||||
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
||||
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
||||
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||
@@ -102,9 +102,9 @@ function Invoke-M365SecurityAudit {
|
||||
[string[]]$IncludeRecommendation,
|
||||
|
||||
# Exclusion of specific recommendation numbers
|
||||
[Parameter(ParameterSetName = 'SkipRecFilter')]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
||||
[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', `
|
||||
'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', `
|
||||
@@ -151,53 +151,30 @@ function Invoke-M365SecurityAudit {
|
||||
# Loop through each required module and assert its availability
|
||||
|
||||
# Establishing connections
|
||||
#if (!($DoNotConnect -or $DoNotTest)) {
|
||||
# Establishing connections
|
||||
if (!($DoNotConnect)) {
|
||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl
|
||||
}
|
||||
|
||||
# Load test definitions from CSV
|
||||
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
||||
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||
|
||||
# Load the Test Definitions into the script scope for use in other functions
|
||||
$script:TestDefinitionsObject = $testDefinitions
|
||||
# Apply filters based on parameter sets
|
||||
switch ($PSCmdlet.ParameterSetName) {
|
||||
'ELevelFilter' {
|
||||
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
|
||||
$testDefinitions = $testDefinitions | Where-Object {
|
||||
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
|
||||
}
|
||||
}
|
||||
elseif ($null -ne $ELevel) {
|
||||
$testDefinitions = $testDefinitions | Where-Object {
|
||||
$_.ELevel -eq $ELevel
|
||||
}
|
||||
}
|
||||
elseif ($null -ne $ProfileLevel) {
|
||||
$testDefinitions = $testDefinitions | Where-Object {
|
||||
$_.ProfileLevel -eq $ProfileLevel
|
||||
}
|
||||
}
|
||||
}
|
||||
'IG1Filter' {
|
||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
|
||||
}
|
||||
'IG2Filter' {
|
||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
|
||||
}
|
||||
'IG3Filter' {
|
||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
|
||||
}
|
||||
'RecFilter' {
|
||||
$testDefinitions = $testDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
|
||||
}
|
||||
'SkipRecFilter' {
|
||||
$testDefinitions = $testDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
|
||||
}
|
||||
$params = @{
|
||||
TestDefinitions = $testDefinitions
|
||||
ParameterSetName = $PSCmdlet.ParameterSetName
|
||||
ELevel = $ELevel
|
||||
ProfileLevel = $ProfileLevel
|
||||
IncludeRecommendation = $IncludeRecommendation
|
||||
SkipRecommendation = $SkipRecommendation
|
||||
}
|
||||
$testDefinitions = Get-TestDefinitionsObject @params
|
||||
# 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
|
||||
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
||||
|
||||
@@ -208,7 +185,7 @@ function Invoke-M365SecurityAudit {
|
||||
} # End Begin
|
||||
|
||||
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
|
||||
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
|
||||
@@ -241,18 +218,30 @@ function Invoke-M365SecurityAudit {
|
||||
Write-Host "Running $functionName..."
|
||||
$result = & $functionName @paramList
|
||||
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
||||
$allAuditResults += $result
|
||||
[void]($allAuditResults.add($Result))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
End {
|
||||
# Return all collected audit results
|
||||
return $allAuditResults
|
||||
# Check if the Disconnect switch is present
|
||||
if (!($DoNotDisconnect)) {
|
||||
# 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
|
||||
1,Test-AntiPhishingPolicy.ps1,2.1.7,E5,L1,FALSE,FALSE,TRUE
|
||||
2,Test-AuditDisabledFalse.ps1,6.1.1,E3,L1,TRUE,TRUE,TRUE
|
||||
3,Test-AuditLogSearch.ps1,3.1.1,E3,L1,TRUE,TRUE,TRUE
|
||||
4,Test-BlockChannelEmails.ps1,8.1.2,E3,L1,FALSE,FALSE,FALSE
|
||||
5,Test-BlockMailForwarding.ps1,6.2.1,E3,L1,FALSE,FALSE,FALSE
|
||||
6,Test-BlockSharedMailboxSignIn.ps1,1.2.2,E3,L1,FALSE,FALSE,FALSE
|
||||
7,Test-CommonAttachmentFilter.ps1,2.1.2,E3,L1,FALSE,TRUE,TRUE
|
||||
8,Test-CustomerLockbox.ps1,1.3.6,E5,L2,FALSE,FALSE,FALSE
|
||||
9,Test-DialInBypassLobby.ps1,8.5.4,E3,L1,FALSE,FALSE,FALSE
|
||||
10,Test-DisallowInfectedFilesDownload.ps1,7.3.1,E5,L2,TRUE,TRUE,TRUE
|
||||
11,Test-EnableDKIM.ps1,2.1.9,E3,L1,FALSE,TRUE,TRUE
|
||||
12,Test-ExternalNoControl.ps1,8.5.7,E3,L1,FALSE,FALSE,FALSE
|
||||
13,Test-ExternalSharingCalendars.ps1,1.3.3,E3,L2,FALSE,TRUE,TRUE
|
||||
14,Test-GlobalAdminsCount.ps1,1.1.3,E3,L1,TRUE,TRUE,TRUE
|
||||
15,Test-GuestAccessExpiration.ps1,7.2.9,E3,L1,FALSE,FALSE,FALSE
|
||||
16,Test-IdentifyExternalEmail.ps1,6.2.3,E3,L1,FALSE,FALSE,FALSE
|
||||
17,Test-LinkSharingRestrictions.ps1,7.2.7,E3,L1,TRUE,TRUE,TRUE
|
||||
18,Test-MailboxAuditingE3.ps1,6.1.2,E3,L1,TRUE,TRUE,TRUE
|
||||
19,Test-MailboxAuditingE5.ps1,6.1.3,E5,L1,TRUE,TRUE,TRUE
|
||||
20,Test-MailTipsEnabled.ps1,6.5.2,E3,L2,FALSE,FALSE,FALSE
|
||||
21,Test-ManagedApprovedPublicGroups.ps1,1.2.1,E3,L2,TRUE,TRUE,TRUE
|
||||
22,Test-MeetingChatNoAnonymous.ps1,8.5.5,E3,L1,FALSE,FALSE,FALSE
|
||||
23,Test-ModernAuthExchangeOnline.ps1,6.5.1,E3,L1,FALSE,TRUE,TRUE
|
||||
24,Test-ModernAuthSharePoint.ps1,7.2.1,E3,L1,FALSE,TRUE,TRUE
|
||||
25,Test-NoAnonymousMeetingJoin.ps1,8.5.1,E3,L2,FALSE,FALSE,FALSE
|
||||
26,Test-NoAnonymousMeetingStart.ps1,8.5.2,E3,L1,FALSE,FALSE,FALSE
|
||||
27,Test-NotifyMalwareInternal.ps1,2.1.3,E3,L1,FALSE,TRUE,TRUE
|
||||
28,Test-NoWhitelistDomains.ps1,6.2.2,E3,L1,FALSE,FALSE,FALSE
|
||||
29,Test-OneDriveContentRestrictions.ps1,7.2.4,E3,L2,TRUE,TRUE,TRUE
|
||||
30,Test-OneDriveSyncRestrictions.ps1,7.3.2,E3,L2,FALSE,FALSE,FALSE
|
||||
31,Test-OrganizersPresent.ps1,8.5.6,E3,L1,FALSE,FALSE,FALSE
|
||||
32,Test-OrgOnlyBypassLobby.ps1,8.5.3,E3,L1,FALSE,FALSE,TRUE
|
||||
33,Test-PasswordHashSync.ps1,5.1.8.1,E3,L1,FALSE,TRUE,TRUE
|
||||
34,Test-PasswordNeverExpirePolicy.ps1,1.3.1,E3,L1,TRUE,TRUE,TRUE
|
||||
35,Test-ReauthWithCode.ps1,7.2.10,E3,L1,FALSE,FALSE,FALSE
|
||||
36,Test-ReportSecurityInTeams.ps1,8.6.1,E3,L1,FALSE,FALSE,FALSE
|
||||
37,Test-RestrictCustomScripts.ps1,7.3.4,E3,L1,FALSE,FALSE,TRUE
|
||||
38,Test-RestrictExternalSharing.ps1,7.2.3,E3,L1,TRUE,TRUE,TRUE
|
||||
39,Test-RestrictOutlookAddins.ps1,6.3.1,E3,L2,FALSE,TRUE,TRUE
|
||||
40,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,E3,L2,TRUE,TRUE,TRUE
|
||||
41,Test-RestrictTenantCreation.ps1,5.1.2.3,E3,L1,FALSE,FALSE,FALSE
|
||||
42,Test-SafeAttachmentsPolicy.ps1,2.1.4,E5,L2,FALSE,FALSE,TRUE
|
||||
43,Test-SafeAttachmentsTeams.ps1,2.1.5,E5,L2,TRUE,TRUE,TRUE
|
||||
44,Test-SafeLinksOfficeApps.ps1,2.1.1,E5,L2,TRUE,TRUE,TRUE
|
||||
45,Test-SharePointAADB2B.ps1,7.2.2,E3,L1,FALSE,FALSE,FALSE
|
||||
46,Test-SharePointExternalSharingDomains.ps1,7.2.6,E3,L2,TRUE,TRUE,TRUE
|
||||
47,Test-SharePointGuestsItemSharing.ps1,7.2.5,E3,L2,TRUE,TRUE,TRUE
|
||||
48,Test-SpamPolicyAdminNotify.ps1,2.1.6,E3,L1,FALSE,TRUE,TRUE
|
||||
49,Test-TeamsExternalAccess.ps1,8.2.1,E3,L2,FALSE,FALSE,FALSE
|
||||
50,Test-TeamsExternalFileSharing.ps1,8.1.1,E3,L2,TRUE,TRUE,TRUE
|
||||
Index,TestFileName,Rec,RecDescription,ELevel,ProfileLevel,CISControl,CISDescription,IG1,IG2,IG3,Automated,Connection
|
||||
1,Test-AdministrativeAccountCompliance.ps1,1.1.1,Ensure Administrative accounts are separate and cloud-only,E3,L1,5.4,Restrict Administrator Privileges to Dedicated Administrator Accounts,TRUE,TRUE,TRUE,FALSE,Microsoft Graph
|
||||
2,Test-GlobalAdminsCount.ps1,1.1.3,Ensure that between two and four global admins are designated,E3,L1,5.1,Establish and Maintain an Inventory of Accounts,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||
3,Test-ManagedApprovedPublicGroups.ps1,1.2.1,Ensure that only organizationally managed/approved public groups exist,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||
4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,AzureAD | EXO
|
||||
5,Test-PasswordNeverExpirePolicy.ps1,1.3.1,Ensure the 'Password expiration policy' is set to 'Set passwords to never expire',E3,L1,5.2,Use Unique Passwords,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||
6,Test-ExternalSharingCalendars.ps1,1.3.3,Ensure 'External sharing' of calendars is not available,E3,L2,4.8,Uninstall or Disable Unnecessary Services on Enterprise Assets and Software,FALSE,TRUE,TRUE,TRUE,EXO
|
||||
7,Test-CustomerLockbox.ps1,1.3.6,Ensure the customer lockbox feature is enabled,E5,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||
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-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-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-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-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-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-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-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-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-RestrictTenantCreation.ps1,5.1.2.3,Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes',E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,Microsoft Graph
|
||||
18,Test-PasswordHashSync.ps1,5.1.8.1,Ensure password hash sync is enabled for hybrid deployments,E3,L1,6.7,Centralize Access Control,FALSE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||
19,Test-AuditDisabledFalse.ps1,6.1.1,Ensure 'AuditDisabled' organizationally is set to 'False',E3,L1,8.2,Collect Audit Logs,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||
20,Test-MailboxAuditingE3.ps1,6.1.2,Ensure mailbox auditing for Office E3 users is Enabled,E3,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO
|
||||
21,Test-MailboxAuditingE5.ps1,6.1.3,Ensure mailbox auditing for Office E5 users is Enabled,E5,L1,8.2,Collect audit logs.,TRUE,TRUE,TRUE,TRUE,AzureAD | EXO
|
||||
22,Test-BlockMailForwarding.ps1,6.2.1,Ensure all forms of mail forwarding are blocked and/or disabled,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||
23,Test-NoWhitelistDomains.ps1,6.2.2,Ensure mail transport rules do not whitelist specific domains,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||
24,Test-IdentifyExternalEmail.ps1,6.2.3,Ensure email from external senders is identified,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO
|
||||
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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
#$auditResults = @()
|
||||
}
|
||||
|
||||
process {
|
||||
# 2.1.7 Ensure that an anti-phishing policy has been created
|
||||
|
||||
# Retrieve and validate the anti-phishing policies
|
||||
$antiPhishPolicies = Get-AntiPhishPolicy
|
||||
@@ -23,37 +26,54 @@ function Test-AntiPhishingPolicy {
|
||||
}
|
||||
|
||||
# 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
|
||||
$failureDetails = if (-not $isCompliant) {
|
||||
"No anti-phishing policy is fully compliant with CIS benchmark requirements."
|
||||
} else {
|
||||
"Compliant Anti-Phish Policy Names: " + ($validatedPolicies.Name -join ', ')
|
||||
# Prepare failure reasons for non-compliant items
|
||||
$nonCompliantNames = $nonCompliantItems | ForEach-Object { $_.Name }
|
||||
$failureReasons = if ($nonCompliantNames.Count -gt 0) {
|
||||
"Reason: Does not meet one or more compliance criteria.`nNon-compliant Policies:`n" + ($nonCompliantNames -join "`n")
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E5"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "2.1.7"
|
||||
$auditResult.RecDescription = "Ensure that an anti-phishing policy has been created"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "9.7"
|
||||
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $isCompliant
|
||||
$auditResult.Details = $failureDetails
|
||||
$auditResult.FailureReason = if (-not $isCompliant) { "Anti-phishing policies do not meet CIS benchmark requirements." } else { "N/A" }
|
||||
# Prepare details for non-compliant items
|
||||
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
|
||||
"Policy: $($_.Name)"
|
||||
}
|
||||
$nonCompliantDetails = $nonCompliantDetails -join "`n"
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return auditResult
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,51 @@
|
||||
function Test-AuditDisabledFalse {
|
||||
[CmdletBinding()]
|
||||
# Aligned
|
||||
param (
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "6.1.1"
|
||||
$auditResult.RecDescription = "Ensure 'AuditDisabled' organizationally is set to 'False'"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "8.2"
|
||||
$auditResult.CISDescription = "Collect Audit Logs"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $auditNotDisabled
|
||||
$auditResult.Details = if ($auditNotDisabled) { "Audit is not disabled organizationally" } else { "Audit is disabled organizationally" }
|
||||
$auditResult.FailureReason = if (-not $auditNotDisabled) { "AuditDisabled is set to True" } else { "N/A" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = 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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,52 @@
|
||||
function Test-AuditLogSearch {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "3.1.1"
|
||||
$auditResult.RecDescription = "Ensure Microsoft 365 audit log search is Enabled"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "8.2"
|
||||
$auditResult.CISDescription = "Collect Audit Logs"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $auditLogResult
|
||||
$auditResult.Details = "UnifiedAuditLogIngestionEnabled: $($auditLogConfig.UnifiedAuditLogIngestionEnabled)"
|
||||
$auditResult.FailureReason = if (-not $auditLogResult) { "Audit log search is not enabled" } else { "N/A" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $auditLogResult) {
|
||||
"Audit log search is not enabled"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$details = if ($auditLogResult) {
|
||||
"UnifiedAuditLogIngestionEnabled: True"
|
||||
}
|
||||
else {
|
||||
"UnifiedAuditLogIngestionEnabled: False"
|
||||
}
|
||||
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "3.1.1"
|
||||
Result = $auditLogResult
|
||||
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
|
||||
$auditResults += $auditResult
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,44 +1,51 @@
|
||||
function Test-BlockChannelEmails {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # This control is Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.1.2"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($allowEmailIntoChannel) {
|
||||
"Emails can be sent to a channel email address"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,51 +1,52 @@
|
||||
function Test-BlockMailForwarding {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned Compare
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Rec = "6.2.1"
|
||||
$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"
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# Verify that no rules are forwarding the email to external domains
|
||||
$transportRules = Get-TransportRule | Where-Object { $_.RedirectMessageTo -ne $null }
|
||||
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||
|
||||
# Retrieve the transport rules that redirect messages
|
||||
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
||||
$forwardingBlocked = $transportRules.Count -eq 0
|
||||
|
||||
$auditResult.Result = $forwardingBlocked
|
||||
$auditResult.Details = if ($transportRules.Count -gt 0) {
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$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 {
|
||||
"$($_.Name) redirects to $($_.RedirectMessageTo)"
|
||||
} -join " | "
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
"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 ', ')"
|
||||
} else {
|
||||
"N/A"
|
||||
|
||||
$params = @{
|
||||
Rec = "6.2.1"
|
||||
Result = $forwardingBlocked
|
||||
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult.Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return the result object
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,47 +1,53 @@
|
||||
function Test-BlockSharedMailboxSignIn {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
|
||||
# Pass if all shared mailboxes have AccountEnabled set to False.
|
||||
# Fail if any shared mailbox has AccountEnabled set to True.
|
||||
|
||||
# Retrieve shared mailbox details
|
||||
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
||||
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
||||
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
||||
$allBlocked = $enabledMailboxes.Count -eq 0
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Control is explicitly not mapped
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "1.2.2"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $false # Control is not mapped, hence IG1 is false
|
||||
$auditResult.IG2 = $false # Control is not mapped, hence IG2 is false
|
||||
$auditResult.IG3 = $false # Control is not mapped, hence IG3 is false
|
||||
$auditResult.RecDescription = "Ensure sign-in to shared mailboxes is blocked"
|
||||
$auditResult.Result = $allBlocked
|
||||
$auditResult.Details = "Enabled Mailboxes: $($enabledMailboxes -join ', ')"
|
||||
$auditResult.FailureReason = if ($allBlocked) { "N/A" } else { "Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')" }
|
||||
$auditResult.Status = if ($allBlocked) { "Pass" } else { "Fail" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $allBlocked) {
|
||||
"Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,46 +1,51 @@
|
||||
function Test-CommonAttachmentFilter {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$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
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = $status
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "2.1.2"
|
||||
$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
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $result) {
|
||||
"Common Attachment Types Filter is disabled"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,40 +1,51 @@
|
||||
function Test-CustomerLockbox {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Define your parameters here
|
||||
# Aligned
|
||||
# Define your parameters here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled
|
||||
|
||||
# Retrieve the organization configuration
|
||||
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
||||
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E5"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.Rec = "1.3.6"
|
||||
$auditResult.RecDescription = "Ensure the customer lockbox feature is enabled"
|
||||
$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." }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $customerLockboxEnabled) {
|
||||
"Customer lockbox feature is not enabled."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,45 +1,51 @@
|
||||
function Test-DialInBypassLobby {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.5.4"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $PSTNBypassDisabled) {
|
||||
"Users dialing in can bypass the lobby"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,41 +1,53 @@
|
||||
function Test-DisallowInfectedFilesDownload {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Define your parameters here
|
||||
# Aligned
|
||||
# Define your parameters here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "10.1"
|
||||
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) {
|
||||
"Downloading infected files is not disallowed."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.3.1"
|
||||
$auditResult.ELevel = "E5"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.RecDescription = "Ensure Office 365 SharePoint infected files are disallowed for download"
|
||||
$details = if ($isDisallowInfectedFileDownloadEnabled) {
|
||||
"DisallowInfectedFileDownload: True"
|
||||
}
|
||||
else {
|
||||
"DisallowInfectedFileDownload: False"
|
||||
}
|
||||
|
||||
# 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 {
|
||||
# Return auditResult
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,44 +1,52 @@
|
||||
function Test-EnableDKIM {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
|
||||
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($dkimResult) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "2.1.9"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = 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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
@@ -1,45 +1,52 @@
|
||||
function Test-ExternalNoControl {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$auditResults = @()
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.5.7"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $externalControlRestricted) {
|
||||
"External participants can give or request control"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,17 +1,21 @@
|
||||
function Test-ExternalSharingCalendars {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$auditResults = @()
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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*' }
|
||||
|
||||
# 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
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Rec = "1.3.3"
|
||||
$auditResult.RecDescription = "Ensure 'External sharing' of calendars is not available"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
# The following IG values are placeholders. Replace with actual values when known.
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.CISControlVer = "v8"
|
||||
# Placeholder for CIS Control, to be replaced with the actual value when available
|
||||
$auditResult.CISControl = "4.8"
|
||||
$auditResult.CISDescription = "Uninstall or Disable Unnecessary Services on Enterprise Assets and Software"
|
||||
$auditResult.Result = $isExternalSharingDisabled
|
||||
$auditResult.Details = if ($isExternalSharingDisabled) { "Calendar sharing with external users is disabled." } else { "Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')" }
|
||||
$auditResult.FailureReason = if ($isExternalSharingDisabled) { "N/A" } else { "Calendar sharing with external users is enabled in one or more policies." }
|
||||
$auditResult.Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isExternalSharingDisabled) {
|
||||
"Calendar sharing with external users is enabled in one or more policies."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,46 +1,52 @@
|
||||
function Test-GlobalAdminsCount {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Define your parameters here
|
||||
# Aligned
|
||||
# Define your parameters here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$auditResults = @()
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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'"
|
||||
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
||||
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
|
||||
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "5.1"
|
||||
$auditResult.CISDescription = "Establish and Maintain an Inventory of Accounts"
|
||||
$auditResult.Rec = "1.1.3"
|
||||
$auditResult.ELevel = "E3" # Based on your environment (E3, E5, etc.)
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $true # Set based on the benchmark
|
||||
$auditResult.IG2 = $true # Set based on the benchmark
|
||||
$auditResult.IG3 = $true # Set based on the benchmark
|
||||
$auditResult.RecDescription = "Ensure 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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($globalAdminCount -lt 2) {
|
||||
"Less than 2 global admins: $globalAdminUsernames"
|
||||
}
|
||||
elseif ($globalAdminCount -gt 4) {
|
||||
"More than 4 global admins: $globalAdminUsernames"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,41 +1,47 @@
|
||||
function Test-GuestAccessExpiration {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Define your parameters here
|
||||
# Aligned
|
||||
# Define your parameters here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isGuestAccessExpirationConfiguredCorrectly) {
|
||||
"Guest access expiration is not configured to automatically expire within 30 days or less."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.2.9"
|
||||
$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"
|
||||
$details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
|
||||
|
||||
$auditResult.Result = $isGuestAccessExpirationConfiguredCorrectly
|
||||
$auditResult.Details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
|
||||
$auditResult.FailureReason = if (-not $isGuestAccessExpirationConfiguredCorrectly) { "Guest access expiration is not configured to automatically expire within 30 days or less." } else { "N/A" }
|
||||
$auditResult.Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.2.9"
|
||||
Result = $isGuestAccessExpirationConfiguredCorrectly
|
||||
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResult
|
||||
# Return the audit result
|
||||
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 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$auditResults = @()
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "6.2.3"
|
||||
$auditResult.RecDescription = "Ensure email from external senders is identified"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $false
|
||||
$auditResult.Result = $externalTaggingEnabled
|
||||
$auditResult.Details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
|
||||
$auditResult.FailureReason = if (-not $externalTaggingEnabled) { "External sender tagging is disabled" } else { "N/A" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = 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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,42 +1,47 @@
|
||||
function Test-LinkSharingRestrictions {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
# Test behavior in prod
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isLinkSharingRestricted) {
|
||||
"Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.2.7"
|
||||
$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"
|
||||
$details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||
|
||||
$auditResult.Result = $isLinkSharingRestricted
|
||||
$auditResult.Details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||
$auditResult.FailureReason = if (-not $isLinkSharingRestricted) { "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)" } else { "N/A" }
|
||||
$auditResult.Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.2.7"
|
||||
Result = $isLinkSharingRestricted
|
||||
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResult
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,54 @@
|
||||
function Test-MailTipsEnabled {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Define your parameters here
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
||||
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
||||
|
||||
# Since there is no direct CIS Control mapping, the control will be set as not applicable.
|
||||
$auditResult.CISControl = "0"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not ($allTipsEnabled -and $externalRecipientsTipsEnabled)) {
|
||||
"One or more MailTips settings are not configured as required."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "6.5.2"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $false
|
||||
$auditResult.RecDescription = "Ensure MailTips are enabled for end users"
|
||||
$details = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) {
|
||||
"MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
||||
}
|
||||
else {
|
||||
"One or more MailTips settings are not configured as required."
|
||||
}
|
||||
|
||||
$auditResult.Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
||||
$auditResult.Details = "MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "One or more MailTips settings are not configured as required." } else { "N/A" }
|
||||
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "6.5.2"
|
||||
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
||||
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResult
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,26 +1,20 @@
|
||||
function Test-MailboxAuditingE3 {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Create Table for Details
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
|
||||
$e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION")
|
||||
$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")
|
||||
$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 = @()
|
||||
$allUsers = Get-AzureADUser -All $true
|
||||
@@ -72,10 +66,19 @@ function Test-MailboxAuditingE3 {
|
||||
}
|
||||
}
|
||||
|
||||
$auditResult.Result = $allFailures.Count -eq 0
|
||||
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
|
||||
$auditResult.Details = if ($auditResult.Result) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
||||
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
||||
$details = if ($allFailures.Count -eq 0) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
||||
|
||||
# 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 {
|
||||
|
@@ -1,23 +1,21 @@
|
||||
function Test-MailboxAuditingE5 {
|
||||
[CmdletBinding()]
|
||||
param ()
|
||||
param (
|
||||
# Aligned
|
||||
# Create Table for Details
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
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")
|
||||
$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")
|
||||
$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 = @()
|
||||
$allUsers = Get-AzureADUser -All $true
|
||||
@@ -31,15 +29,11 @@ function Test-MailboxAuditingE5 {
|
||||
}
|
||||
|
||||
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
|
||||
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
||||
if ($hasOfficeE5) {
|
||||
$userUPN = $user.UserPrincipalName
|
||||
|
||||
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
||||
|
||||
$missingActions = @()
|
||||
@@ -78,13 +72,19 @@ function Test-MailboxAuditingE5 {
|
||||
}
|
||||
}
|
||||
|
||||
if ($allFailures.Count -eq 0) {
|
||||
Write-Verbose "All evaluated E5 users have correct mailbox audit settings."
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
||||
$details = if ($allFailures.Count -eq 0) { "All Office E5 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
||||
|
||||
# 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.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" }
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,54 +1,52 @@
|
||||
function Test-ManagedApprovedPublicGroups {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Define your parameters here
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
|
||||
$auditResults = @()
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
|
||||
# Check if there are public groups and if they are organizationally managed/approved
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
$auditResult.Rec = "1.2.1"
|
||||
$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"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {
|
||||
"There are public groups present that are not organizationally managed/approved."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
|
||||
$auditResult.Result = $true
|
||||
$auditResult.Details = "No public groups found."
|
||||
$auditResult.FailureReason = "N/A"
|
||||
$auditResult.Status = "Pass"
|
||||
$details = if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
|
||||
"No public groups found."
|
||||
}
|
||||
else {
|
||||
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
||||
$detailsString = $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"
|
||||
"Public groups found: $($groupDetails -join ', ')"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,46 +1,48 @@
|
||||
function Test-MeetingChatNoAnonymous {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
|
||||
# Retrieve the Teams meeting policy for meeting chat
|
||||
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
||||
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.5.5"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($chatAnonDisabled) {
|
||||
"N/A"
|
||||
}
|
||||
else {
|
||||
"Meeting chat allows anonymous users"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,40 +1,42 @@
|
||||
function Test-ModernAuthExchangeOnline {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResults = [CISAuditResult]::new()
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
try {
|
||||
# Ensuring the ExchangeOnlineManagement module is available
|
||||
|
||||
|
||||
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
||||
$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"
|
||||
$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" }
|
||||
$details = "OAuth2ClientProfileEnabled: $($orgConfig.OAuth2ClientProfileEnabled) for Organization: $($orgConfig.Name)"
|
||||
|
||||
# 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 {
|
||||
@@ -43,12 +45,7 @@ function Test-ModernAuthExchangeOnline {
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -1,13 +1,14 @@
|
||||
function Test-ModernAuthSharePoint {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
@@ -15,25 +16,29 @@ function Test-ModernAuthSharePoint {
|
||||
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
||||
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.10"
|
||||
$auditResult.CISDescription = "Encrypt Sensitive Data in Transit"
|
||||
$auditResult.Rec = "7.2.1"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.RecDescription = "Modern Authentication for SharePoint Applications"
|
||||
$auditResult.Result = $modernAuthForSPRequired
|
||||
$auditResult.Details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)"
|
||||
$auditResult.FailureReason = if (-not $modernAuthForSPRequired) { "Legacy authentication protocols are enabled" } else { "N/A" }
|
||||
$auditResult.Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $modernAuthForSPRequired) {
|
||||
"Legacy authentication protocols are enabled"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)"
|
||||
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.2.1"
|
||||
Result = $modernAuthForSPRequired
|
||||
Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResult
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,14 @@
|
||||
function Test-NoAnonymousMeetingJoin {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
@@ -18,28 +19,29 @@ function Test-NoAnonymousMeetingJoin {
|
||||
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
|
||||
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.5.1"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
||||
$auditResult.RecDescription = "Ensure 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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($allowAnonymousUsersToJoinMeeting) {
|
||||
"Anonymous users are allowed to join meetings"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,14 @@
|
||||
function Test-NoAnonymousMeetingStart {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
@@ -18,28 +19,29 @@ function Test-NoAnonymousMeetingStart {
|
||||
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
|
||||
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.5.2"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($anonymousStartDisabled) {
|
||||
"N/A"
|
||||
}
|
||||
else {
|
||||
"Anonymous users and dial-in callers can start a meeting"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,47 @@
|
||||
function Test-NoWhitelistDomains {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
# Retrieve transport rules that whitelist specific domains
|
||||
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
|
||||
|
||||
$auditResult.Rec = "6.2.2"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$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"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($whitelistedRules) {
|
||||
"There are transport rules whitelisting specific domains."
|
||||
}
|
||||
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 {
|
||||
|
@@ -1,15 +1,19 @@
|
||||
function Test-NotifyMalwareInternal {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
||||
|
||||
# Retrieve all 'Custom' malware filter policies and check notification settings
|
||||
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
||||
$policiesToReport = @()
|
||||
@@ -22,31 +26,35 @@ function Test-NotifyMalwareInternal {
|
||||
|
||||
# Determine the result based on the presence of custom policies without notifications
|
||||
$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
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "2.1.3"
|
||||
$auditResult.RecDescription = "Ensure notifications for internal users sending malware is Enabled"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "17.5"
|
||||
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $result
|
||||
$auditResult.Details = $details
|
||||
$auditResult.FailureReason = $failureReason
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($result) {
|
||||
"N/A"
|
||||
}
|
||||
else {
|
||||
"Some custom policies do not have notifications for internal users sending malware enabled."
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,47 @@
|
||||
function Test-OneDriveContentRestrictions {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||
|
||||
# Retrieve OneDrive sharing capability settings
|
||||
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
||||
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isOneDriveSharingRestricted) {
|
||||
"OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.2.4"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.RecDescription = "Ensure OneDrive content sharing is restricted"
|
||||
$details = if ($isOneDriveSharingRestricted) {
|
||||
"OneDrive content sharing is restricted."
|
||||
}
|
||||
else {
|
||||
"OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
||||
}
|
||||
|
||||
$auditResult.Result = $isOneDriveSharingRestricted
|
||||
$auditResult.Details = "OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
||||
$auditResult.FailureReason = if (-not $isOneDriveSharingRestricted) { "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)" } else { "N/A" }
|
||||
$auditResult.Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.2.4"
|
||||
Result = $isOneDriveSharingRestricted
|
||||
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,37 +1,47 @@
|
||||
function Test-OneDriveSyncRestrictions {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isSyncRestricted) {
|
||||
"OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.3.2"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $false
|
||||
$auditResult.RecDescription = "Ensure OneDrive sync is restricted for unmanaged devices"
|
||||
$details = if ($isSyncRestricted) {
|
||||
"OneDrive sync is restricted for unmanaged devices."
|
||||
}
|
||||
else {
|
||||
"TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
||||
}
|
||||
|
||||
$auditResult.Result = $isSyncRestricted
|
||||
$auditResult.Details = "TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
||||
$auditResult.FailureReason = if (-not $isSyncRestricted) { "OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs." } else { "N/A" }
|
||||
$auditResult.Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.3.2"
|
||||
Result = $isSyncRestricted
|
||||
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,13 +1,14 @@
|
||||
function Test-OrgOnlyBypassLobby {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
@@ -15,31 +16,38 @@ function Test-OrgOnlyBypassLobby {
|
||||
|
||||
# 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
|
||||
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "6.8"
|
||||
$auditResult.CISDescription = "Define and Maintain Role-Based Access Control"
|
||||
$auditResult.Rec = "8.5.3"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $lobbyBypassRestricted) {
|
||||
"External participants can bypass the lobby"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,14 @@
|
||||
function Test-OrganizersPresent {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
@@ -15,31 +16,38 @@ function Test-OrganizersPresent {
|
||||
|
||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||
|
||||
# Retrieve the Teams meeting policy for presenters
|
||||
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
|
||||
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.5.6"
|
||||
$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" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $presenterRoleRestricted) {
|
||||
"Others besides organizers and co-organizers can present"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,43 +1,47 @@
|
||||
function Test-PasswordHashSync {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
||||
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
|
||||
$passwordHashSync = Get-MgOrganization | Select-Object OnPremisesSyncEnabled
|
||||
$hashSyncResult = $passwordHashSync.OnPremisesSyncEnabled
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
|
||||
$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" }
|
||||
# Retrieve password hash sync status
|
||||
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
|
||||
$hashSyncResult = $passwordHashSync
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,45 +1,47 @@
|
||||
function Test-PasswordNeverExpirePolicy {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
[Parameter(Mandatory)]
|
||||
[string]$DomainName # DomainName parameter is now mandatory
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
|
||||
# Pass if PasswordValidityPeriodInDays is 0.
|
||||
# Fail otherwise.
|
||||
# Pass if PasswordValidityPeriodInDays is 0. 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
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Rec = "1.3.1"
|
||||
$auditResult.RecDescription = "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "5.2"
|
||||
$auditResult.CISDescription = "Use Unique Passwords"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true # All are true
|
||||
$auditResult.Result = $passwordPolicy.PasswordValidityPeriodInDays -eq 0
|
||||
$auditResult.Details = "Validity Period: $($passwordPolicy.PasswordValidityPeriodInDays) days"
|
||||
$auditResult.FailureReason = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "N/A" } else { "Password expiration is not set to never expire" }
|
||||
$auditResult.Status = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "Pass" } else { "Fail" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($passwordPolicy -ne 0) {
|
||||
"Password expiration is not set to never expire"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,42 @@
|
||||
function Test-ReauthWithCode {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||
|
||||
# Retrieve reauthentication settings for SharePoint Online
|
||||
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
||||
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isReauthenticationRestricted) {
|
||||
"Reauthentication with verification code does not require reauthentication within 15 days or less."
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.2.10"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $false
|
||||
$auditResult.RecDescription = "Ensure reauthentication with verification code is restricted"
|
||||
$details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
||||
|
||||
$auditResult.Result = $isReauthenticationRestricted
|
||||
$auditResult.Details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
||||
$auditResult.FailureReason = if (-not $isReauthenticationRestricted) { "Reauthentication with verification code does not require reauthentication within 15 days or less." } else { "N/A" }
|
||||
$auditResult.Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.2.10"
|
||||
Result = $isReauthenticationRestricted
|
||||
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,21 +1,20 @@
|
||||
function Test-ReportSecurityInTeams {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 8.6.1 (L1) Ensure users can report security concerns in Teams
|
||||
|
||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||
# Connect to Exchange Online PowerShell using Connect-ExchangeOnline
|
||||
|
||||
# Retrieve the necessary settings for Teams and Exchange Online
|
||||
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
|
||||
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
||||
|
||||
@@ -25,32 +24,33 @@ function Test-ReportSecurityInTeams {
|
||||
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
||||
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.6.1"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
||||
$auditResult.RecDescription = "Ensure users can report security concerns in Teams"
|
||||
$auditResult.Result = $securityReportEnabled
|
||||
$auditResult.Details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $securityReportEnabled) {
|
||||
"Users cannot report security concerns in Teams due to one or more incorrect settings"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
|
||||
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
|
||||
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
|
||||
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
|
||||
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
|
||||
$auditResult.FailureReason = if (-not $securityReportEnabled) { "Users cannot report security concerns in Teams due to one or more incorrect settings" } else { "N/A" }
|
||||
$auditResult.Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
||||
|
||||
$auditResults += $auditResult
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "8.6.1"
|
||||
Result = $securityReportEnabled
|
||||
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return auditResult
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,21 +1,20 @@
|
||||
function Test-RestrictCustomScripts {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# 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 {
|
||||
# CIS 2.7 Ensure custom script execution is restricted on site collections
|
||||
# Pass if DenyAddAndCustomizePages is set to true (Enabled). Fail otherwise.
|
||||
# 7.3.4 (L1) Ensure custom script execution is restricted on site collections
|
||||
|
||||
# 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
|
||||
|
||||
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
|
||||
@@ -29,42 +28,34 @@ function Test-RestrictCustomScripts {
|
||||
"$($_.Title) ($($_.Url)): Custom Script Allowed"
|
||||
}
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$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) {
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $complianceResult) {
|
||||
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
$auditResult.Status = if ($complianceResult) {
|
||||
"Pass"
|
||||
} else {
|
||||
"Fail"
|
||||
|
||||
$details = if ($complianceResult) {
|
||||
"All site collections have custom script execution restricted"
|
||||
}
|
||||
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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return auditResult
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,37 +1,42 @@
|
||||
function Test-RestrictExternalSharing {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 7.2.3 (L1) Ensure external content sharing is restricted
|
||||
|
||||
# Retrieve the SharingCapability setting for the SharePoint tenant
|
||||
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
||||
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if (-not $isRestricted) {
|
||||
"External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)"
|
||||
}
|
||||
else {
|
||||
"N/A"
|
||||
}
|
||||
|
||||
$auditResult.Rec = "7.2.3"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.RecDescription = "Ensure external content sharing is restricted"
|
||||
$details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
||||
|
||||
$auditResult.Result = $isRestricted
|
||||
$auditResult.Details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
||||
$auditResult.FailureReason = if (-not $isRestricted) { "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)" } else { "N/A" }
|
||||
$auditResult.Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "7.2.3"
|
||||
Result = $isRestricted
|
||||
Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,20 +1,20 @@
|
||||
function Test-RestrictOutlookAddins {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters could include credentials or other necessary data
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$customPolicyFailures = @()
|
||||
$defaultPolicyFailureDetails = @()
|
||||
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
|
||||
}
|
||||
|
||||
process {
|
||||
# Main functionality
|
||||
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
||||
|
||||
# Check all mailboxes for custom policies with unallowed add-ins
|
||||
@@ -38,24 +38,11 @@ function Test-RestrictOutlookAddins {
|
||||
if ($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 = ""
|
||||
if ($customPolicyFailures) {
|
||||
$detailsString += "Custom Policy Failures: | "
|
||||
# Use pipes or tabs here instead of newlines
|
||||
$detailsString += ($customPolicyFailures -join " | ")
|
||||
}
|
||||
else {
|
||||
@@ -70,20 +57,22 @@ function Test-RestrictOutlookAddins {
|
||||
$detailsString += "Compliant"
|
||||
}
|
||||
|
||||
if ($customPolicyFailures -or $defaultPolicyFailureDetails) {
|
||||
$auditResult.Result = $false
|
||||
$auditResult.Status = "Fail"
|
||||
$auditResult.Details = $detailsString
|
||||
$auditResult.FailureReason = "Unauthorized Outlook add-ins found in custom or default policies."
|
||||
# Determine result based on findings
|
||||
$isCompliant = -not ($customPolicyFailures -or $defaultPolicyFailureDetails)
|
||||
|
||||
# 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." }
|
||||
}
|
||||
else {
|
||||
$auditResult.Result = $true
|
||||
$auditResult.Status = "Pass"
|
||||
$auditResult.Details = "No unauthorized Outlook add-ins found in custom or default policies."
|
||||
$auditResult.FailureReason = "N/A"
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
# Return auditResult
|
||||
end {
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
@@ -1,48 +1,56 @@
|
||||
function Test-RestrictStorageProvidersOutlook {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
$auditResult.Rec = "6.5.3"
|
||||
$auditResult.ELevel = "E3" # Based on your environment
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$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 ', ')"
|
||||
# Retrieve all OwaMailbox policies
|
||||
$owaPolicies = Get-OwaMailboxPolicy
|
||||
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable }
|
||||
|
||||
# Determine compliance
|
||||
$allPoliciesRestricted = $nonCompliantPolicies.Count -eq 0
|
||||
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($allPoliciesRestricted) {
|
||||
"N/A"
|
||||
}
|
||||
$auditResult.FailureReason = if(-not $allPoliciesRestricted) { "One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable." } else { "N/A" }
|
||||
$auditResult.Status = if($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
||||
else {
|
||||
"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 {
|
||||
# Return auditResult
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Additional helper functions (if any)
|
||||
|
||||
|
@@ -1,43 +1,48 @@
|
||||
function Test-RestrictTenantCreation {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 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
|
||||
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "5.1.2.3"
|
||||
$auditResult.RecDescription = "Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $false
|
||||
$auditResult.Result = $tenantCreationResult
|
||||
$auditResult.Details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
|
||||
$auditResult.FailureReason = if (-not $tenantCreationResult) { "Non-admin users can create tenants" } else { "N/A" }
|
||||
# Prepare failure reasons and details based on compliance
|
||||
$failureReasons = if ($tenantCreationResult) {
|
||||
"N/A"
|
||||
}
|
||||
else {
|
||||
"Non-admin users can create tenants"
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
# Additional helper functions (if any)
|
||||
|
@@ -1,49 +1,53 @@
|
||||
function Test-SafeAttachmentsPolicy {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
||||
|
||||
# Retrieve all Safe Attachment policies where Enable is set to True
|
||||
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
|
||||
|
||||
# If there are any enabled policies, the result is Pass. If not, it's Fail.
|
||||
$result = $safeAttachmentPolicies -ne $null -and $safeAttachmentPolicies.Count -gt 0
|
||||
# Determine result and details based on the presence of enabled policies
|
||||
$result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
|
||||
$details = if ($result) {
|
||||
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
"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
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E5"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.Rec = "2.1.4"
|
||||
$auditResult.RecDescription = "Ensure Safe Attachments policy is enabled"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "9.7"
|
||||
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $result
|
||||
$auditResult.Details = $details
|
||||
$auditResult.FailureReason = $failureReason
|
||||
$failureReasons = if ($result) {
|
||||
"N/A"
|
||||
}
|
||||
else {
|
||||
"Safe Attachments policy is not enabled."
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
# Additional helper functions (if any)
|
||||
|
@@ -1,16 +1,19 @@
|
||||
function Test-SafeAttachmentsTeams {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
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
|
||||
$atpPolicies = Get-AtpPolicyForO365
|
||||
|
||||
@@ -25,33 +28,33 @@ function Test-SafeAttachmentsTeams {
|
||||
$result = $null -ne $atpPolicyResult
|
||||
$details = if ($result) {
|
||||
"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."
|
||||
}
|
||||
$failureReason = if ($result) { "N/A" } else { "ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured." }
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E5"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.Rec = "2.1.5"
|
||||
$auditResult.RecDescription = "Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "9.7, 10.1"
|
||||
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $result
|
||||
$auditResult.Details = $details
|
||||
$auditResult.FailureReason = $failureReason
|
||||
$failureReasons = if ($result) {
|
||||
"N/A"
|
||||
}
|
||||
else {
|
||||
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
||||
}
|
||||
|
||||
$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 {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
# Additional helper functions (if any)
|
||||
|
@@ -1,16 +1,19 @@
|
||||
function Test-SafeLinksOfficeApps {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
|
||||
$auditResults = @()
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
}
|
||||
|
||||
process {
|
||||
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||
|
||||
# Retrieve all Safe Links policies
|
||||
$policies = Get-SafeLinksPolicy
|
||||
|
||||
@@ -42,29 +45,21 @@ function Test-SafeLinksOfficeApps {
|
||||
# Prepare the final result
|
||||
$result = $misconfiguredDetails.Count -eq 0
|
||||
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
||||
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
||||
|
||||
# Create the audit result object
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E5"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.Rec = "2.1.1"
|
||||
$auditResult.RecDescription = "Ensure Safe Links for Office Applications is Enabled"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "10.1"
|
||||
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $result
|
||||
$auditResult.Details = $details
|
||||
$auditResult.FailureReason = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
||||
|
||||
$auditResults += $auditResult
|
||||
# Create and populate the CISAuditResult object
|
||||
$params = @{
|
||||
Rec = "2.1.1"
|
||||
Result = $result
|
||||
Status = if ($result) { "Pass" } else { "Fail" }
|
||||
Details = $details
|
||||
FailureReason = $failureReasons
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return the audit result
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,14 @@
|
||||
function Test-SharePointAADB2B {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
}
|
||||
@@ -15,22 +18,14 @@ function Test-SharePointAADB2B {
|
||||
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0"
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
|
||||
$auditResult.Rec = "7.2.2"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $false
|
||||
$auditResult.IG3 = $false
|
||||
$auditResult.RecDescription = "Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled"
|
||||
|
||||
$auditResult.Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
||||
$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" }
|
||||
$params = @{
|
||||
Rec = "7.2.2"
|
||||
Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
||||
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
||||
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
||||
FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,11 +1,14 @@
|
||||
function Test-SharePointExternalSharingDomains {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
}
|
||||
@@ -16,22 +19,14 @@ function Test-SharePointExternalSharingDomains {
|
||||
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
|
||||
$auditResult.Rec = "7.2.6"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $true
|
||||
$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" }
|
||||
$params = @{
|
||||
Rec = "7.2.6"
|
||||
Result = $isDomainRestrictionConfigured
|
||||
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
||||
Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
|
||||
FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,11 +1,14 @@
|
||||
function Test-SharePointGuestsItemSharing {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Define your parameters here
|
||||
)
|
||||
|
||||
begin {
|
||||
# Initialization code
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
}
|
||||
@@ -16,22 +19,14 @@ function Test-SharePointGuestsItemSharing {
|
||||
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
|
||||
|
||||
# Populate the auditResult object with the required properties
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
|
||||
$auditResult.Rec = "7.2.5"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $true
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.RecDescription = "Ensure that SharePoint guest users cannot share items they don't own"
|
||||
|
||||
$auditResult.Result = $isGuestResharingPrevented
|
||||
$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" }
|
||||
$params = @{
|
||||
Rec = "7.2.5"
|
||||
Result = $isGuestResharingPrevented
|
||||
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
||||
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
||||
FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
|
@@ -1,15 +1,21 @@
|
||||
function Test-SpamPolicyAdminNotify {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added if needed
|
||||
)
|
||||
|
||||
begin {
|
||||
# Dot source the class script if necessary
|
||||
#. .\source\Classes\CISAuditResult.ps1
|
||||
# Initialization code, if needed
|
||||
|
||||
$auditResults = @()
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
}
|
||||
|
||||
process {
|
||||
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
|
||||
|
||||
# Get the default hosted outbound spam filter policy
|
||||
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
||||
|
||||
@@ -28,29 +34,18 @@ function Test-SpamPolicyAdminNotify {
|
||||
}
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L1"
|
||||
$auditResult.Rec = "2.1.6"
|
||||
$auditResult.RecDescription = "Ensure Exchange Online Spam Policies are set to notify administrators"
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "17.5"
|
||||
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
|
||||
$auditResult.IG1 = $false
|
||||
$auditResult.IG2 = $true
|
||||
$auditResult.IG3 = $true
|
||||
$auditResult.Result = $areSettingsEnabled
|
||||
$auditResult.Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
|
||||
$auditResult.FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
|
||||
|
||||
$auditResults += $auditResult
|
||||
$params = @{
|
||||
Rec = "2.1.6"
|
||||
Result = $areSettingsEnabled
|
||||
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
||||
Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
|
||||
FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return auditResult
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -1,13 +1,16 @@
|
||||
function Test-TeamsExternalAccess {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be defined here if needed
|
||||
)
|
||||
|
||||
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 {
|
||||
@@ -26,27 +29,18 @@ function Test-TeamsExternalAccess {
|
||||
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
|
||||
$auditResult.CISDescription = "Explicitly Not Mapped"
|
||||
$auditResult.Rec = "8.2.1"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $false # Set based on the CIS Controls image
|
||||
$auditResult.IG2 = $false # Set based on the CIS Controls image
|
||||
$auditResult.IG3 = $false # Set based on the CIS Controls image
|
||||
$auditResult.RecDescription = "Ensure 'external access' is restricted in the Teams admin center"
|
||||
$auditResult.Result = $isCompliant
|
||||
$auditResult.Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
|
||||
$auditResult.FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
|
||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||
|
||||
$auditResults += $auditResult
|
||||
$params = @{
|
||||
Rec = "8.2.1"
|
||||
Result = $isCompliant
|
||||
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
|
||||
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return auditResult
|
||||
return $auditResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,13 +1,16 @@
|
||||
function Test-TeamsExternalFileSharing {
|
||||
[CmdletBinding()]
|
||||
param (
|
||||
# Aligned
|
||||
# Parameters can be added here if needed
|
||||
)
|
||||
|
||||
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 {
|
||||
@@ -30,27 +33,18 @@ function Test-TeamsExternalFileSharing {
|
||||
}
|
||||
|
||||
# Create an instance of CISAuditResult and populate it
|
||||
$auditResult = [CISAuditResult]::new()
|
||||
$auditResult.CISControlVer = "v8"
|
||||
$auditResult.CISControl = "3.3"
|
||||
$auditResult.CISDescription = "Configure Data Access Control Lists"
|
||||
$auditResult.Rec = "8.1.1"
|
||||
$auditResult.ELevel = "E3"
|
||||
$auditResult.ProfileLevel = "L2"
|
||||
$auditResult.IG1 = $true # Set based on the benchmark
|
||||
$auditResult.IG2 = $true # Set based on the benchmark
|
||||
$auditResult.IG3 = $true # Set based on the benchmark
|
||||
$auditResult.RecDescription = "Ensure external file sharing in Teams is enabled for only approved cloud storage services"
|
||||
$auditResult.Result = $isCompliant
|
||||
$auditResult.Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
||||
$auditResult.FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
||||
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||
|
||||
$auditResults += $auditResult
|
||||
$params = @{
|
||||
Rec = "8.1.1"
|
||||
Result = $isCompliant
|
||||
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
||||
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
||||
}
|
||||
$auditResult = Initialize-CISAuditResult @params
|
||||
}
|
||||
|
||||
end {
|
||||
# Return auditResults
|
||||
return $auditResults
|
||||
# Return auditResult
|
||||
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