Compare commits
118 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
c2cc980a91 | ||
|
4b3e448e48 | ||
|
342d0ac4a9 | ||
|
d4252a1839 | ||
|
1fde9947e0 | ||
|
da856b96e4 | ||
|
8835ddfbfd | ||
|
9a7de2f549 | ||
|
c9940c2a09 | ||
|
83332207b4 | ||
|
ccacf76e6c | ||
|
273630839e | ||
|
3ca779650e | ||
|
0cde0ae5e2 | ||
|
cd3162aad0 | ||
|
9dba7c2be8 | ||
|
b123bd4155 | ||
|
10471b4683 | ||
|
cf7fbadbe7 | ||
|
e6b6e064bf | ||
|
f85101d0de | ||
|
f880e566ea | ||
|
7041b0ba52 | ||
|
1161baffad | ||
|
032c951e02 | ||
|
6ed99dbacf | ||
|
30c848e74d | ||
|
40193bd492 | ||
|
5c868a20fc | ||
|
4db0fd3742 | ||
|
83a8e31aa5 | ||
|
b9de0638bb | ||
|
5a0475c253 | ||
|
312aabc81c | ||
|
e6da6d9d47 | ||
|
014c42b3fe | ||
|
fbfb5b5986 | ||
|
03b5bb47e2 | ||
|
9dc99636d3 | ||
|
afe657ffc0 | ||
|
702f557579 | ||
|
f855ef7d0b | ||
|
270e980a57 | ||
|
ff90669984 | ||
|
f2e799af2f | ||
|
4a4d200197 | ||
|
9199d97fc2 | ||
|
5d681f3d72 | ||
|
f926c63533 | ||
|
d5044f0bf4 | ||
|
055ab42261 | ||
|
0d97b95c6c | ||
|
c185878674 | ||
|
61063ee63c | ||
|
4115f1e83e | ||
|
0b3213d957 | ||
|
9d9b9e70d9 | ||
|
4167a37121 | ||
|
66536e34a7 | ||
|
db9b206ae3 | ||
|
8a9044486b | ||
|
447be9cacb | ||
|
71c798c52a | ||
|
d0270027f9 | ||
|
0569fd98cc | ||
|
c842ae9720 | ||
|
63b9e8b75d | ||
|
f7b87ebc78 | ||
|
b70da1845f | ||
|
ef55447e67 | ||
|
7a9d2885f3 | ||
|
4ab5affc9f | ||
|
5871294210 | ||
|
a5dc7f1ebd | ||
|
c0222ef3bc | ||
|
83ee6c2ac3 | ||
|
cbdb31c7c5 | ||
|
a5d26917d3 | ||
|
4f8df29c72 | ||
|
bb1df11128 | ||
|
2d9a1a1d10 | ||
|
41dbf2f0db | ||
|
70dcd74643 | ||
|
f6aa4b83dd | ||
|
d58d0b664d | ||
|
b9a8a75945 | ||
|
6495073a10 | ||
|
e0e2a04b6a | ||
|
d85968935b | ||
|
f47efa74f9 | ||
|
f905f269d1 | ||
|
8719900af7 | ||
|
8922ea12cd | ||
|
c6bdad0477 | ||
|
85bd0fb19f | ||
|
8de61dda9f | ||
|
c530d2df76 | ||
|
f5ab68dd63 | ||
|
d9ed3b60b7 | ||
|
4857aead5e | ||
|
92e5952f7e | ||
|
cb52ce1a73 | ||
|
4bc1f8fdda | ||
|
8446d101a7 | ||
|
06aeadaee5 | ||
|
a9e44a7c6f | ||
|
2712f78412 | ||
|
df89e23bc1 | ||
|
2027e8b21b | ||
|
5c60f39dad | ||
|
399288b10a | ||
|
87d0aaaea6 | ||
|
8301b8cdbe | ||
|
3f4166e98a | ||
|
7582511dd5 | ||
|
20afb8d83e | ||
|
c378f5d119 | ||
|
3a37d465e8 |
50
.github/workflows/powershell.yml
vendored
Normal file
50
.github/workflows/powershell.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# This workflow uses actions that are not certified by GitHub.
|
||||||
|
# They are provided by a third-party and are governed by
|
||||||
|
# separate terms of service, privacy policy, and support
|
||||||
|
# documentation.
|
||||||
|
#
|
||||||
|
# https://github.com/microsoft/action-psscriptanalyzer
|
||||||
|
# For more information on PSScriptAnalyzer in general, see
|
||||||
|
# https://github.com/PowerShell/PSScriptAnalyzer
|
||||||
|
|
||||||
|
name: PSScriptAnalyzer
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
schedule:
|
||||||
|
- cron: '33 20 * * 4'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
permissions:
|
||||||
|
contents: read # for actions/checkout to fetch code
|
||||||
|
security-events: write # for github/codeql-action/upload-sarif to upload SARIF results
|
||||||
|
actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status
|
||||||
|
name: PSScriptAnalyzer
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Run PSScriptAnalyzer
|
||||||
|
uses: microsoft/psscriptanalyzer-action@6b2948b1944407914a58661c49941824d149734f
|
||||||
|
with:
|
||||||
|
# Check https://github.com/microsoft/action-psscriptanalyzer for more info about the options.
|
||||||
|
# The below set up runs PSScriptAnalyzer to your entire repository and runs some basic security rules.
|
||||||
|
path: .\source
|
||||||
|
recurse: true
|
||||||
|
# Include your own basic security rules. Removing this option will run all the rules
|
||||||
|
includeRule: '"PSAvoidGlobalAliases", "PSAvoidUsingConvertToSecureStringWithPlainText", "PSAvoidUsingPlainTextForPassword", "PSAvoidUsingInvokeExpression", "PSUseApprovedVerbs", "PSAvoidUsingPositionalParameters", "PSAvoidUsingEmptyCatchBlock", "PSAvoidUsingDeprecatedManifestFields", "PSAvoidUsingUserNameAndPasswordParams", "PSAvoidUsingCmdletAliases"'
|
||||||
|
|
||||||
|
output: results.sarif
|
||||||
|
|
||||||
|
# Upload the SARIF file generated in the previous step
|
||||||
|
- name: Upload SARIF results file
|
||||||
|
uses: github/codeql-action/upload-sarif@v2
|
||||||
|
with:
|
||||||
|
sarif_file: results.sarif
|
111
CHANGELOG.md
111
CHANGELOG.md
@@ -6,6 +6,114 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Added Get-MFAStatus function to help with auditing mfa for conditional access controls.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed 6.1.2/6.1.3 tests to minimize calls to the Graph API.
|
||||||
|
- Fixed 2.1.1,2.1.4,2.1.5 to suppress error messages and create a standard object when no e5"
|
||||||
|
|
||||||
|
## [0.1.10] - 2024-06-12
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added condition comments to each test.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed csv CIS controls that were not matched correctly.
|
||||||
|
|
||||||
|
## [0.1.9] - 2024-06-10
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fixed bug in 1.1.1 that caused the test to fail/pass incorrectly. Added verbose output.
|
||||||
|
|
||||||
|
### Docs
|
||||||
|
|
||||||
|
- Updated helper csv formatting for one cis control.
|
||||||
|
|
||||||
|
|
||||||
|
## [0.1.8] - 2024-06-09
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added output type to functions.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Whatif support for `Invoke-M365SecurityAudit`.
|
||||||
|
- Whatif module output and module install process.
|
||||||
|
|
||||||
|
## [0.1.7] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated `Connect-M365Suite` to make `TenantAdminUrl` an optional parameter.
|
||||||
|
- Updated `Invoke-M365SecurityAudit` to make `TenantAdminUrl` an optional parameter.
|
||||||
|
- Improved connection handling and error messaging in `Connect-M365Suite`.
|
||||||
|
- Enhanced `Invoke-M365SecurityAudit` to allow flexible inclusion and exclusion of specific recommendations, IG filters, and profile levels.
|
||||||
|
- SupportsShoudProcess to also bypass connection checks in `Invoke-M365SecurityAudit` as well as Disconnect-M365Suite.
|
||||||
|
|
||||||
|
## [0.1.6] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added pipeline support to `Sync-CISExcelAndCsvData` function for `[CISAuditResult[]]` input.
|
||||||
|
|
||||||
|
## [0.1.5] - 2024-06-08
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Updated test definitions for CIS Microsoft 365 Foundations Benchmark for better error handling and object output when errors occur.
|
||||||
|
- Added a parameter to the `Initialize-CISAuditResult` function to allow for a static failed object to be created when an error occurs.
|
||||||
|
- Refactored `Invoke-M365SecurityAudit` to include a new private function `Invoke-TestFunction` for executing test functions and handling errors.
|
||||||
|
- Added a new private function `Measure-AuditResult` to calculate and display audit results.
|
||||||
|
- Enhanced error logging to capture failed test details and display them at the end of the audit.
|
||||||
|
- Added a private function `Get-RequiredModule` to initialize the `$requiredModules` variable for better code organization in the main script.
|
||||||
|
- Updated `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to use `Format-MissingAction` for structuring missing actions into a pipe-separated table format.
|
||||||
|
- Added more verbose logging to `Test-BlockMailForwarding` and improved error handling for better troubleshooting.
|
||||||
|
- Improved `Test-RestrictCustomScripts` to handle long URL lengths better by extracting and replacing common hostnames, and provided detailed output.
|
||||||
|
- Added sorting to output.
|
||||||
|
- Created new functions for improved modularity.
|
||||||
|
- Parameter validation for Excel and CSV path in sync function.
|
||||||
|
- Added Output type to tests.
|
||||||
|
- Added `M365DomainForPWPolicyTest` parameter to `Invoke-M365SecurityAudit` to specify testing only the default domain for password expiration policy when '1.3.1' is included in the tests.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Ensured the `Invoke-TestFunction` returns a `CISAuditResult` object, which is then managed in the `Invoke-M365SecurityAudit` function.
|
||||||
|
- Corrected the usage of the join operation within `$details` in `Test-BlockMailForwarding` to handle arrays properly.
|
||||||
|
- Fixed the logic in `Test-RestrictCustomScripts` to accurately replace and manage URLs, ensuring compliance checks are correctly performed.
|
||||||
|
- Updated the `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` functions to handle the `$allFailures` variable correctly, ensuring accurate pass/fail results.
|
||||||
|
- Fixed the connections in helper CSV and connect function.
|
||||||
|
- Removed verbose preference from `Test-RestrictCustomScripts`.
|
||||||
|
- Ensured that the output in `Test-BlockMailForwarding` does not include extra spaces between table headers and data.
|
||||||
|
- Fixed output in `Test-MailboxAuditingE3` and `Test-MailboxAuditingE5` to correctly align with the new table format.
|
||||||
|
- Added step 1 and step 2 in `Test-BlockMailForwarding` details to ensure comprehensive compliance checks.
|
||||||
|
- Fixed the issue with the output in `Test-RestrictCustomScripts` to ensure no extra spaces between table headers and data.
|
||||||
|
|
||||||
|
## [0.1.4] - 2024-05-30
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Test definitions filter function.
|
||||||
|
- Logging function for future use.
|
||||||
|
- Test grade written to console.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Updated sync function to include connection info.
|
||||||
|
- Refactored connect/disconnect functions to evaluate needed connections.
|
||||||
|
|
||||||
|
## [0.1.3] - 2024-05-28
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
- Array list to store the results of the audit.
|
- Array list to store the results of the audit.
|
||||||
- Arraylist tests and helper template.
|
- Arraylist tests and helper template.
|
||||||
- New testing function.
|
- New testing function.
|
||||||
@@ -27,7 +135,6 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
|
|
||||||
- Updated comments and documentation for new functions.
|
- Updated comments and documentation for new functions.
|
||||||
|
|
||||||
|
|
||||||
## [0.1.2] - 2024-04-29
|
## [0.1.2] - 2024-04-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -75,4 +182,4 @@ The format is based on and uses the types of changes according to [Keep a Change
|
|||||||
- A dynamic test loading system based on CSV input for flexibility in defining audit tests.
|
- A dynamic test loading system based on CSV input for flexibility in defining audit tests.
|
||||||
- Comprehensive verbose logging to detail the steps being performed during an audit.
|
- Comprehensive verbose logging to detail the steps being performed during an audit.
|
||||||
- Comment-help documentation for the `Invoke-M365SecurityAudit` function with examples and usage details.
|
- Comment-help documentation for the `Invoke-M365SecurityAudit` function with examples and usage details.
|
||||||
- Attribution to CIS and licensing information under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License in the README.
|
- Attribution to CIS and licensing information under the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License in the README.
|
||||||
|
18
SECURITY.md
Normal file
18
SECURITY.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
Use this section to tell people about which versions of your project are
|
||||||
|
currently being supported with security updates.
|
||||||
|
|
||||||
|
| Version | Supported |
|
||||||
|
| ------- | ------------------ |
|
||||||
|
| 0.1.4 | :white_check_mark: |
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
Use this section to tell people how to report a vulnerability.
|
||||||
|
|
||||||
|
Tell them where to go, how often they can expect to get an update on a
|
||||||
|
reported vulnerability, what to expect if the vulnerability is accepted or
|
||||||
|
declined, etc.
|
BIN
docs/index.html
BIN
docs/index.html
Binary file not shown.
@@ -4,14 +4,82 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
|||||||
|
|
||||||
|
|
||||||
<#
|
<#
|
||||||
$ver = "v0.1.2"
|
$ver = "v0.1.10"
|
||||||
git checkout main
|
git checkout main
|
||||||
git pull origin main
|
git pull origin main
|
||||||
git tag -a $ver -m "Release version $ver Bugfix Update"
|
git tag -a $ver -m "Release version $ver refactor Update"
|
||||||
git push origin $ver
|
git push origin $ver
|
||||||
"Fix: PR #37"
|
"Fix: PR #37"
|
||||||
git push origin $ver
|
git push origin $ver
|
||||||
# git tag -d $ver
|
# git tag -d $ver
|
||||||
#>
|
#>
|
||||||
|
|
||||||
|
# Refresh authentication to ensure the correct scopes
|
||||||
|
gh auth refresh -s project,read:project,write:project,repo
|
||||||
|
|
||||||
|
# Create the project
|
||||||
|
gh project create --owner CriticalSolutionsNetwork --title "Test Validation Project"
|
||||||
|
|
||||||
|
$repoOwner = "CriticalSolutionsNetwork"
|
||||||
|
$repoName = "M365FoundationsCISReport"
|
||||||
|
$directoryPath = ".\source\tests"
|
||||||
|
$projectName = "Test Validation Project"
|
||||||
|
|
||||||
|
# Function to create GitHub issues
|
||||||
|
function Create-GitHubIssue {
|
||||||
|
param (
|
||||||
|
[string]$title,
|
||||||
|
[string]$body,
|
||||||
|
[string]$project
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the issue and add it to the specified project
|
||||||
|
$issue = gh issue create --repo "$repoOwner/$repoName" --title "$title" --body "$body" --project "$project"
|
||||||
|
return $issue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load test definitions from CSV
|
||||||
|
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
|
||||||
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
|
|
||||||
|
# Iterate over each .ps1 file in the directory
|
||||||
|
Get-ChildItem -Path $directoryPath -Filter "*.ps1" | ForEach-Object {
|
||||||
|
$fileName = $_.Name
|
||||||
|
$testDefinition = $testDefinitions | Where-Object { $_.TestFileName -eq $fileName }
|
||||||
|
|
||||||
|
if ($testDefinition) {
|
||||||
|
$rec = $testDefinition.Rec
|
||||||
|
$elevel = $testDefinition.ELevel
|
||||||
|
$profileLevel = $testDefinition.ProfileLevel
|
||||||
|
$ig1 = $testDefinition.IG1
|
||||||
|
$ig2 = $testDefinition.IG2
|
||||||
|
$ig3 = $testDefinition.IG3
|
||||||
|
$connection = $testDefinition.Connection
|
||||||
|
|
||||||
|
$issueTitle = "Rec: $rec - Validate $fileName, ELevel: $elevel, ProfileLevel: $profileLevel, IG1: $ig1, IG2: $ig2, IG3: $ig3, Connection: $connection"
|
||||||
|
$issueBody = @"
|
||||||
|
# Validation for $fileName
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [ ] Validate test for a pass
|
||||||
|
- Description of passing criteria:
|
||||||
|
- [ ] Validate test for a fail
|
||||||
|
- Description of failing criteria:
|
||||||
|
- [ ] Add notes and observations
|
||||||
|
- Placeholder for additional notes:
|
||||||
|
"@
|
||||||
|
|
||||||
|
# Create the issue using GitHub CLI
|
||||||
|
try {
|
||||||
|
Create-GitHubIssue -title "$issueTitle" -body "$issueBody" -project "$projectName"
|
||||||
|
Write-Output "Created issue for $fileName"
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to create issue for $fileName : $_"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Introduce a delay of 2 seconds
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
} else {
|
||||||
|
Write-Warning "No matching test definition found for $fileName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -60,7 +60,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
if ($missingActions) {
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
$formattedActions = Format-MissingAction $missingActions
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
||||||
}
|
}
|
||||||
# Mark the user as processed
|
# Mark the user as processed
|
||||||
@@ -83,7 +83,7 @@ function Test-MailboxAuditingE3_6.1.2_E3L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
function Format-MissingAction {
|
||||||
param ([array]$missingActions)
|
param ([array]$missingActions)
|
||||||
|
|
||||||
$actionGroups = @{
|
$actionGroups = @{
|
||||||
|
@@ -60,7 +60,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
if ($missingActions) {
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
$formattedActions = Format-MissingAction $missingActions
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -92,7 +92,7 @@ function Test-MailboxAuditingE5_6.1.3_E5L1_IG1_IG2_IG3 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
function Format-MissingAction {
|
||||||
param ([array]$missingActions)
|
param ([array]$missingActions)
|
||||||
|
|
||||||
$actionGroups = @{
|
$actionGroups = @{
|
||||||
|
@@ -1,33 +1,37 @@
|
|||||||
function Assert-ModuleAvailability {
|
function Assert-ModuleAvailability {
|
||||||
|
[OutputType([void]) ]
|
||||||
param(
|
param(
|
||||||
[string]$ModuleName,
|
[string]$ModuleName,
|
||||||
[string]$RequiredVersion,
|
[string]$RequiredVersion,
|
||||||
[string]$SubModuleName
|
[string[]]$SubModules = @()
|
||||||
)
|
)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
|
||||||
|
|
||||||
if ($null -eq $module) {$auditResult.Profile
|
if ($null -eq $module) {
|
||||||
Write-Host "Installing $ModuleName module..."
|
Write-Information "Installing $ModuleName module..." -InformationAction Continue
|
||||||
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
|
||||||
}
|
}
|
||||||
elseif ($module.Version -lt [version]$RequiredVersion) {
|
elseif ($module.Version -lt [version]$RequiredVersion) {
|
||||||
Write-Host "Updating $ModuleName module to required version..."
|
Write-Information "Updating $ModuleName module to required version..." -InformationAction Continue
|
||||||
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Host "$ModuleName module is already at required version or newer."
|
Write-Information "$ModuleName module is already at required version or newer." -InformationAction Continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($SubModuleName) {
|
if ($SubModules.Count -gt 0) {
|
||||||
Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
foreach ($subModule in $SubModules) {
|
||||||
}
|
Write-Information "Importing submodule $ModuleName.$subModule..." -InformationAction Continue
|
||||||
else {
|
Import-Module -Name "$ModuleName.$subModule" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Information "Importing module $ModuleName..." -InformationAction Continue
|
||||||
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "An error occurred with module $ModuleName`: $_"
|
Write-Warning "An error occurred with module $ModuleName`: $_"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,56 +1,59 @@
|
|||||||
function Connect-M365Suite {
|
function Connect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
# Parameter to specify the SharePoint Online Tenant Admin URL
|
[Parameter(Mandatory=$false)]
|
||||||
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
[Parameter(Mandatory)]
|
[Parameter(Mandatory)]
|
||||||
[string]$TenantAdminUrl
|
[string[]]$RequiredConnections
|
||||||
)
|
)
|
||||||
$VerbosePreference = "SilentlyContinue"
|
|
||||||
|
$VerbosePreference = "SilentlyContinue"
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
|
||||||
|
Connect-AzureAD | Out-Null
|
||||||
|
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
# Attempt to connect to Azure Active Directory
|
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "EXO | Microsoft Graph") {
|
||||||
Write-Host "Connecting to Azure Active Directory..." -ForegroundColor Cyan
|
|
||||||
Connect-AzureAD | Out-Null
|
|
||||||
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
|
||||||
|
|
||||||
# Attempt to connect to Exchange Online
|
|
||||||
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
|
||||||
Connect-ExchangeOnline | Out-Null
|
|
||||||
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
|
||||||
try {
|
|
||||||
# Attempt to connect to Microsoft Graph with specified scopes
|
|
||||||
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
||||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
try {
|
||||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
||||||
}
|
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||||
catch {
|
}
|
||||||
Write-Host "Failed to connect o MgGraph, attempting device auth." -ForegroundColor Yellow
|
catch {
|
||||||
# Attempt to connect to Microsoft Graph with specified scopes
|
Write-Host "Failed to connect to MgGraph, attempting device auth." -ForegroundColor Yellow
|
||||||
Write-Host "Connecting to Microsoft Graph using device auth with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
|
||||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
|
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
||||||
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Validate SharePoint Online Tenant Admin URL
|
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "EXO | Microsoft Graph") {
|
||||||
if (-not $TenantAdminUrl) {
|
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
||||||
throw "SharePoint Online Tenant Admin URL is required."
|
Connect-ExchangeOnline | Out-Null
|
||||||
|
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
||||||
}
|
}
|
||||||
|
|
||||||
# Attempt to connect to SharePoint Online
|
if ($RequiredConnections -contains "SPO") {
|
||||||
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
|
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
|
||||||
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
Connect-SPOService -Url $TenantAdminUrl | Out-Null
|
||||||
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
|
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
|
||||||
|
}
|
||||||
|
|
||||||
# Attempt to connect to Microsoft Teams
|
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||||
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
|
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
|
||||||
Connect-MicrosoftTeams | Out-Null
|
Connect-MicrosoftTeams | Out-Null
|
||||||
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
|
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
$VerbosePreference = "Continue"
|
$VerbosePreference = "Continue"
|
||||||
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
|
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
|
||||||
throw $_
|
throw $_
|
||||||
}
|
}
|
||||||
|
|
||||||
$VerbosePreference = "Continue"
|
$VerbosePreference = "Continue"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,39 +1,60 @@
|
|||||||
function Disconnect-M365Suite {
|
function Disconnect-M365Suite {
|
||||||
|
[OutputType([void])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
[string[]]$RequiredConnections
|
||||||
|
)
|
||||||
|
|
||||||
# Clean up sessions
|
# Clean up sessions
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
|
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||||
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
|
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
|
||||||
|
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Exchange Online: $_"
|
Write-Warning "Failed to disconnect from Exchange Online: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
|
if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO") {
|
||||||
Disconnect-AzureAD | Out-Null
|
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
|
||||||
|
Disconnect-AzureAD | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Azure AD: $_"
|
Write-Warning "Failed to disconnect from Azure AD: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
if ($RequiredConnections -contains "Microsoft Graph") {
|
||||||
Disconnect-MgGraph | Out-Null
|
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
|
||||||
|
Disconnect-MgGraph | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
|
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
|
if ($RequiredConnections -contains "SPO") {
|
||||||
Disconnect-SPOService | Out-Null
|
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
|
||||||
|
Disconnect-SPOService | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from SharePoint Online: $_"
|
Write-Warning "Failed to disconnect from SharePoint Online: $_"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
|
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") {
|
||||||
Disconnect-MicrosoftTeams | Out-Null
|
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
|
||||||
|
Disconnect-MicrosoftTeams | Out-Null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
|
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
|
||||||
}
|
}
|
||||||
Write-Host "All sessions have been disconnected." -ForegroundColor Green
|
|
||||||
|
Write-Host "All necessary sessions have been disconnected." -ForegroundColor Green
|
||||||
}
|
}
|
29
source/Private/Format-MissingAction.ps1
Normal file
29
source/Private/Format-MissingAction.ps1
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
function Format-MissingAction {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([hashtable])]
|
||||||
|
param (
|
||||||
|
[array]$missingActions
|
||||||
|
)
|
||||||
|
|
||||||
|
$actionGroups = @{
|
||||||
|
"Admin" = @()
|
||||||
|
"Delegate" = @()
|
||||||
|
"Owner" = @()
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($action in $missingActions) {
|
||||||
|
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
|
||||||
|
$type = $matches[1]
|
||||||
|
$actionName = $matches[2]
|
||||||
|
$actionGroups[$type] += $actionName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$formattedResults = @{
|
||||||
|
Admin = $actionGroups["Admin"] -join ', '
|
||||||
|
Delegate = $actionGroups["Delegate"] -join ', '
|
||||||
|
Owner = $actionGroups["Owner"] -join ', '
|
||||||
|
}
|
||||||
|
|
||||||
|
return $formattedResults
|
||||||
|
}
|
19
source/Private/Format-RequiredModuleList.ps1
Normal file
19
source/Private/Format-RequiredModuleList.ps1
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
function Format-RequiredModuleList {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[System.Object[]]$RequiredModules
|
||||||
|
)
|
||||||
|
|
||||||
|
$requiredModulesFormatted = ""
|
||||||
|
foreach ($module in $RequiredModules) {
|
||||||
|
if ($module.SubModules -and $module.SubModules.Count -gt 0) {
|
||||||
|
$subModulesFormatted = $module.SubModules -join ', '
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName) (SubModules: $subModulesFormatted), "
|
||||||
|
} else {
|
||||||
|
$requiredModulesFormatted += "$($module.ModuleName), "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $requiredModulesFormatted.TrimEnd(", ")
|
||||||
|
}
|
24
source/Private/Get-MostCommonWord.ps1
Normal file
24
source/Private/Get-MostCommonWord.ps1
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
function Get-MostCommonWord {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$InputStrings
|
||||||
|
)
|
||||||
|
|
||||||
|
# Combine all strings into one large string
|
||||||
|
$allText = $InputStrings -join ' '
|
||||||
|
|
||||||
|
# Split the large string into words
|
||||||
|
$words = $allText -split '\s+'
|
||||||
|
|
||||||
|
# Group words and count occurrences
|
||||||
|
$wordGroups = $words | Group-Object | Sort-Object Count -Descending
|
||||||
|
|
||||||
|
# Return the most common word if it occurs at least 3 times
|
||||||
|
if ($wordGroups.Count -gt 0 -and $wordGroups[0].Count -ge 3) {
|
||||||
|
return $wordGroups[0].Name
|
||||||
|
} else {
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
}
|
31
source/Private/Get-RequiredModule.ps1
Normal file
31
source/Private/Get-RequiredModule.ps1
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
function Get-RequiredModule {
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'AuditFunction')]
|
||||||
|
[OutputType([System.Object[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'AuditFunction')]
|
||||||
|
[switch]$AuditFunction,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'SyncFunction')]
|
||||||
|
[switch]$SyncFunction
|
||||||
|
)
|
||||||
|
|
||||||
|
switch ($PSCmdlet.ParameterSetName) {
|
||||||
|
'AuditFunction' {
|
||||||
|
return @(
|
||||||
|
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() },
|
||||||
|
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() },
|
||||||
|
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("Groups", "DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") },
|
||||||
|
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() },
|
||||||
|
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
'SyncFunction' {
|
||||||
|
return @(
|
||||||
|
@{ ModuleName = "ImportExcel"; RequiredVersion = "7.8.9"; SubModules = @() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
default {
|
||||||
|
throw "Please specify either -AuditFunction or -SyncFunction switch."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
65
source/Private/Get-TestDefinitionsObject.ps1
Normal file
65
source/Private/Get-TestDefinitionsObject.ps1
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
function Get-TestDefinitionsObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([object[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[object[]]$TestDefinitions,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string]$ParameterSetName,
|
||||||
|
|
||||||
|
[string]$ELevel,
|
||||||
|
[string]$ProfileLevel,
|
||||||
|
[string[]]$IncludeRecommendation,
|
||||||
|
[string[]]$SkipRecommendation
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Verbose "Initial test definitions count: $($TestDefinitions.Count)"
|
||||||
|
|
||||||
|
switch ($ParameterSetName) {
|
||||||
|
'ELevelFilter' {
|
||||||
|
Write-Verbose "Applying ELevelFilter"
|
||||||
|
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
|
||||||
|
Write-Verbose "Filtering on ELevel = $ELevel and ProfileLevel = $ProfileLevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($null -ne $ELevel) {
|
||||||
|
Write-Verbose "Filtering on ELevel = $ELevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ELevel -eq $ELevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elseif ($null -ne $ProfileLevel) {
|
||||||
|
Write-Verbose "Filtering on ProfileLevel = $ProfileLevel"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object {
|
||||||
|
$_.ProfileLevel -eq $ProfileLevel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'IG1Filter' {
|
||||||
|
Write-Verbose "Applying IG1Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'IG2Filter' {
|
||||||
|
Write-Verbose "Applying IG2Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'IG3Filter' {
|
||||||
|
Write-Verbose "Applying IG3Filter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
|
||||||
|
}
|
||||||
|
'RecFilter' {
|
||||||
|
Write-Verbose "Applying RecFilter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
|
||||||
|
}
|
||||||
|
'SkipRecFilter' {
|
||||||
|
Write-Verbose "Applying SkipRecFilter"
|
||||||
|
$TestDefinitions = $TestDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose "Filtered test definitions count: $($TestDefinitions.Count)"
|
||||||
|
return $TestDefinitions
|
||||||
|
}
|
28
source/Private/Get-UniqueConnection.ps1
Normal file
28
source/Private/Get-UniqueConnection.ps1
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
function Get-UniqueConnection {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([string[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[string[]]$Connections
|
||||||
|
)
|
||||||
|
|
||||||
|
$uniqueConnections = @()
|
||||||
|
|
||||||
|
if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "AzureAD"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "Microsoft Graph"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") {
|
||||||
|
$uniqueConnections += "EXO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "SPO") {
|
||||||
|
$uniqueConnections += "SPO"
|
||||||
|
}
|
||||||
|
if ($Connections -contains "Microsoft Teams" -or $Connections -contains "Microsoft Teams | EXO") {
|
||||||
|
$uniqueConnections += "Microsoft Teams"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uniqueConnections | Sort-Object -Unique
|
||||||
|
}
|
@@ -1,19 +1,24 @@
|
|||||||
function Initialize-CISAuditResult {
|
function Initialize-CISAuditResult {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$Rec,
|
[string]$Rec,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
[bool]$Result,
|
[bool]$Result,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
[string]$Status,
|
[string]$Status,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
[string]$Details,
|
[string]$Details,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true, ParameterSetName = 'Full')]
|
||||||
[string]$FailureReason
|
[string]$FailureReason,
|
||||||
|
|
||||||
|
[Parameter(ParameterSetName = 'Error')]
|
||||||
|
[switch]$Failure
|
||||||
)
|
)
|
||||||
|
|
||||||
# Import the test definitions CSV file
|
# Import the test definitions CSV file
|
||||||
@@ -22,6 +27,10 @@ function Initialize-CISAuditResult {
|
|||||||
# Find the row that matches the provided recommendation (Rec)
|
# Find the row that matches the provided recommendation (Rec)
|
||||||
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $Rec }
|
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $Rec }
|
||||||
|
|
||||||
|
if (-not $testDefinition) {
|
||||||
|
throw "Test definition for recommendation '$Rec' not found."
|
||||||
|
}
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Create an instance of CISAuditResult and populate it
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
$auditResult.Rec = $Rec
|
$auditResult.Rec = $Rec
|
||||||
@@ -36,10 +45,18 @@ function Initialize-CISAuditResult {
|
|||||||
$auditResult.Automated = [bool]::Parse($testDefinition.Automated)
|
$auditResult.Automated = [bool]::Parse($testDefinition.Automated)
|
||||||
$auditResult.Connection = $testDefinition.Connection
|
$auditResult.Connection = $testDefinition.Connection
|
||||||
$auditResult.CISControlVer = 'v8'
|
$auditResult.CISControlVer = 'v8'
|
||||||
$auditResult.Result = $Result
|
|
||||||
$auditResult.Status = $Status
|
if ($PSCmdlet.ParameterSetName -eq 'Full') {
|
||||||
$auditResult.Details = $Details
|
$auditResult.Result = $Result
|
||||||
$auditResult.FailureReason = $FailureReason
|
$auditResult.Status = $Status
|
||||||
|
$auditResult.Details = $Details
|
||||||
|
$auditResult.FailureReason = $FailureReason
|
||||||
|
} elseif ($PSCmdlet.ParameterSetName -eq 'Error') {
|
||||||
|
$auditResult.Result = $false
|
||||||
|
$auditResult.Status = 'Fail'
|
||||||
|
$auditResult.Details = "An error occurred while processing the test."
|
||||||
|
$auditResult.FailureReason = "Initialization error: Failed to process the test."
|
||||||
|
}
|
||||||
|
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
|
35
source/Private/Invoke-TestFunction.ps1
Normal file
35
source/Private/Invoke-TestFunction.ps1
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
function Invoke-TestFunction {
|
||||||
|
[OutputType([CISAuditResult[]])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[PSObject]$FunctionFile,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[string]$DomainName
|
||||||
|
)
|
||||||
|
|
||||||
|
$functionName = $FunctionFile.BaseName
|
||||||
|
$functionCmd = Get-Command -Name $functionName
|
||||||
|
|
||||||
|
# Check if the test function needs DomainName parameter
|
||||||
|
$paramList = @{}
|
||||||
|
if ('DomainName' -in $functionCmd.Parameters.Keys) {
|
||||||
|
$paramList.DomainName = $DomainName
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use splatting to pass parameters
|
||||||
|
Write-Verbose "Running $functionName..."
|
||||||
|
try {
|
||||||
|
$result = & $functionName @paramList
|
||||||
|
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Test = $functionName; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $functionName -Failure
|
||||||
|
return $auditResult
|
||||||
|
}
|
||||||
|
}
|
32
source/Private/Measure-AuditResult.ps1
Normal file
32
source/Private/Measure-AuditResult.ps1
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
function Measure-AuditResult {
|
||||||
|
[OutputType([void])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[System.Collections.ArrayList]$AllAuditResults,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[System.Collections.ArrayList]$FailedTests
|
||||||
|
)
|
||||||
|
|
||||||
|
# Calculate the total number of tests
|
||||||
|
$totalTests = $AllAuditResults.Count
|
||||||
|
|
||||||
|
# Calculate the number of passed tests
|
||||||
|
$passedTests = $AllAuditResults.ToArray() | Where-Object { $_.Result -eq $true } | Measure-Object | Select-Object -ExpandProperty Count
|
||||||
|
|
||||||
|
# Calculate the pass percentage
|
||||||
|
$passPercentage = if ($totalTests -eq 0) { 0 } else { [math]::Round(($passedTests / $totalTests) * 100, 2) }
|
||||||
|
|
||||||
|
# Display the pass percentage to the user
|
||||||
|
Write-Host "Audit completed. $passedTests out of $totalTests tests passed." -ForegroundColor Cyan
|
||||||
|
Write-Host "Your passing percentage is $passPercentage%."
|
||||||
|
|
||||||
|
# Display details of failed tests
|
||||||
|
if ($FailedTests.Count -gt 0) {
|
||||||
|
Write-Host "The following tests failed to complete:" -ForegroundColor Red
|
||||||
|
foreach ($failedTest in $FailedTests) {
|
||||||
|
Write-Host "Test: $($failedTest.Test)" -ForegroundColor Yellow
|
||||||
|
Write-Host "Error: $($failedTest.Error)" -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,6 @@
|
|||||||
function Merge-CISExcelAndCsvData {
|
function Merge-CISExcelAndCsvData {
|
||||||
[CmdletBinding()]
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
|
[OutputType([PSCustomObject[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$ExcelPath,
|
[string]$ExcelPath,
|
||||||
@@ -7,41 +8,35 @@ function Merge-CISExcelAndCsvData {
|
|||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$WorksheetName,
|
[string]$WorksheetName,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
|
||||||
[string]$CsvPath
|
[string]$CsvPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput')]
|
||||||
|
[CISAuditResult[]]$AuditResults
|
||||||
)
|
)
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Import data from Excel and CSV
|
# Import data from Excel
|
||||||
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
|
$import = Import-Excel -Path $ExcelPath -WorksheetName $WorksheetName
|
||||||
$csvData = Import-Csv -Path $CsvPath
|
|
||||||
|
|
||||||
# Define a function to create a merged object
|
# Import data from CSV or use provided object
|
||||||
function CreateMergedObject($excelItem, $csvRow) {
|
$csvData = if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
|
||||||
$newObject = New-Object PSObject
|
Import-Csv -Path $CsvPath
|
||||||
|
} else {
|
||||||
foreach ($property in $excelItem.PSObject.Properties) {
|
$AuditResults
|
||||||
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
$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
|
# Iterate over each item in the imported Excel object and merge with CSV data or audit results
|
||||||
$mergedData = foreach ($item in $import) {
|
$mergedData = foreach ($item in $import) {
|
||||||
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
|
$csvRow = $csvData | Where-Object { $_.Rec -eq $item.'recommendation #' }
|
||||||
if ($csvRow) {
|
if ($csvRow) {
|
||||||
CreateMergedObject -excelItem $item -csvRow $csvRow
|
New-MergedObject -ExcelItem $item -CsvRow $csvRow
|
||||||
} else {
|
} else {
|
||||||
CreateMergedObject -excelItem $item -csvRow ([PSCustomObject]@{Status=$null; Details=$null; FailureReason=$null})
|
New-MergedObject -ExcelItem $item -CsvRow ([PSCustomObject]@{Connection=$null;Status=$null; Details=$null; FailureReason=$null })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Return the merged data
|
# Return the merged data
|
||||||
return $mergedData
|
return $mergedData
|
||||||
}
|
}
|
||||||
}
|
}
|
22
source/Private/New-MergedObject.ps1
Normal file
22
source/Private/New-MergedObject.ps1
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
function New-MergedObject {
|
||||||
|
[CmdletBinding()]
|
||||||
|
[OutputType([PSCustomObject])]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[psobject]$ExcelItem,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true)]
|
||||||
|
[psobject]$CsvRow
|
||||||
|
)
|
||||||
|
|
||||||
|
$newObject = New-Object PSObject
|
||||||
|
|
||||||
|
foreach ($property in $ExcelItem.PSObject.Properties) {
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name $property.Name -Value $property.Value
|
||||||
|
}
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Connection' -Value $CsvRow.Connection
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Status' -Value $CsvRow.Status
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_Details' -Value $CsvRow.Details
|
||||||
|
$newObject | Add-Member -MemberType NoteProperty -Name 'CSV_FailureReason' -Value $CsvRow.FailureReason
|
||||||
|
return $newObject
|
||||||
|
}
|
22
source/Private/Test-IsAdmin.ps1
Normal file
22
source/Private/Test-IsAdmin.ps1
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
function Test-IsAdmin {
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Checks if the current user is an administrator on the machine.
|
||||||
|
.DESCRIPTION
|
||||||
|
This private function returns a Boolean value indicating whether
|
||||||
|
the current user has administrator privileges on the machine.
|
||||||
|
It does this by creating a new WindowsPrincipal object, passing
|
||||||
|
in a WindowsIdentity object representing the current user, and
|
||||||
|
then checking if that principal is in the Administrator role.
|
||||||
|
.INPUTS
|
||||||
|
None.
|
||||||
|
.OUTPUTS
|
||||||
|
Boolean. Returns True if the current user is an administrator, and False otherwise.
|
||||||
|
.EXAMPLE
|
||||||
|
PS C:\> Test-IsAdmin
|
||||||
|
True
|
||||||
|
#>
|
||||||
|
|
||||||
|
# Create a new WindowsPrincipal object for the current user and check if it is in the Administrator role
|
||||||
|
(New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
|
||||||
|
}
|
@@ -1,4 +1,5 @@
|
|||||||
function Update-CISExcelWorksheet {
|
function Update-CISExcelWorksheet {
|
||||||
|
[OutputType([void])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
@@ -25,7 +26,7 @@ function Update-CISExcelWorksheet {
|
|||||||
|
|
||||||
|
|
||||||
# Update the worksheet with the provided data
|
# Update the worksheet with the provided data
|
||||||
Update-WorksheetCells -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
|
Update-WorksheetCell -Worksheet $worksheet -Data $Data -StartingRowIndex $StartingRowIndex
|
||||||
|
|
||||||
# Save and close the Excel package
|
# Save and close the Excel package
|
||||||
Close-ExcelPackage $excelPackage
|
Close-ExcelPackage $excelPackage
|
||||||
|
29
source/Private/Update-WorksheetCell.ps1
Normal file
29
source/Private/Update-WorksheetCell.ps1
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
function Update-WorksheetCell {
|
||||||
|
[OutputType([void])]
|
||||||
|
param (
|
||||||
|
$Worksheet,
|
||||||
|
$Data,
|
||||||
|
$StartingRowIndex
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check and set headers
|
||||||
|
$firstItem = $Data[0]
|
||||||
|
$colIndex = 1
|
||||||
|
foreach ($property in $firstItem.PSObject.Properties) {
|
||||||
|
if ($StartingRowIndex -eq 2 -and $Worksheet.Cells[1, $colIndex].Value -eq $null) {
|
||||||
|
$Worksheet.Cells[1, $colIndex].Value = $property.Name
|
||||||
|
}
|
||||||
|
$colIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
# Iterate over each row in the data and update cells
|
||||||
|
$rowIndex = $StartingRowIndex
|
||||||
|
foreach ($item in $Data) {
|
||||||
|
$colIndex = 1
|
||||||
|
foreach ($property in $item.PSObject.Properties) {
|
||||||
|
$Worksheet.Cells[$rowIndex, $colIndex].Value = $property.Value
|
||||||
|
$colIndex++
|
||||||
|
}
|
||||||
|
$rowIndex++
|
||||||
|
}
|
||||||
|
}
|
@@ -1,28 +0,0 @@
|
|||||||
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)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,8 @@
|
|||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-AdminRoleUserLicense
|
||||||
#>
|
#>
|
||||||
function Get-AdminRoleUserLicense {
|
function Get-AdminRoleUserLicense {
|
||||||
|
# Set output type to System.Collections.ArrayList
|
||||||
|
[OutputType([System.Collections.ArrayList])]
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $false)]
|
[Parameter(Mandatory = $false)]
|
||||||
|
103
source/Public/Get-MFAStatus.ps1
Normal file
103
source/Public/Get-MFAStatus.ps1
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Retrieves the MFA (Multi-Factor Authentication) status for Azure Active Directory users.
|
||||||
|
.DESCRIPTION
|
||||||
|
The Get-MFAStatus function connects to Microsoft Online Service and retrieves the MFA status for all Azure Active Directory users, excluding guest accounts. Optionally, you can specify a single user by their User Principal Name (UPN) to get their MFA status.
|
||||||
|
.PARAMETER UserId
|
||||||
|
The User Principal Name (UPN) of a specific user to retrieve MFA status for. If not provided, the function retrieves MFA status for all users.
|
||||||
|
.EXAMPLE
|
||||||
|
Get-MFAStatus
|
||||||
|
Retrieves the MFA status for all Azure Active Directory users.
|
||||||
|
.EXAMPLE
|
||||||
|
Get-MFAStatus -UserId "example@domain.com"
|
||||||
|
Retrieves the MFA status for the specified user with the UPN "example@domain.com".
|
||||||
|
.OUTPUTS
|
||||||
|
System.Object
|
||||||
|
Returns a sorted list of custom objects containing the following properties:
|
||||||
|
- UserPrincipalName
|
||||||
|
- DisplayName
|
||||||
|
- MFAState
|
||||||
|
- MFADefaultMethod
|
||||||
|
- MFAPhoneNumber
|
||||||
|
- PrimarySMTP
|
||||||
|
- Aliases
|
||||||
|
.NOTES
|
||||||
|
The function requires the MSOL module to be installed and connected to your tenant.
|
||||||
|
Ensure that you have the necessary permissions to read user and MFA status information.
|
||||||
|
.LINK
|
||||||
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Get-MFAStatus
|
||||||
|
#>
|
||||||
|
function Get-MFAStatus {
|
||||||
|
[OutputType([System.Object])]
|
||||||
|
[CmdletBinding()]
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory = $false)]
|
||||||
|
[ValidateNotNullOrEmpty()]
|
||||||
|
[string]$UserId
|
||||||
|
)
|
||||||
|
|
||||||
|
begin {
|
||||||
|
# Connect to Microsoft Online service
|
||||||
|
Import-Module MSOnline -ErrorAction SilentlyContinue
|
||||||
|
}
|
||||||
|
|
||||||
|
process {
|
||||||
|
if (Get-Module MSOnline){
|
||||||
|
Connect-MsolService
|
||||||
|
Write-Host -Object "Finding Azure Active Directory Accounts..."
|
||||||
|
# Get all users, excluding guests
|
||||||
|
$Users = if ($PSBoundParameters.ContainsKey('UserId')) {
|
||||||
|
Get-MsolUser -UserPrincipalName $UserId
|
||||||
|
} else {
|
||||||
|
Get-MsolUser -All | Where-Object { $_.UserType -ne "Guest" }
|
||||||
|
}
|
||||||
|
$Report = [System.Collections.Generic.List[Object]]::new() # Create output list
|
||||||
|
Write-Host -Object "Processing" $Users.Count "accounts..."
|
||||||
|
ForEach ($User in $Users) {
|
||||||
|
$MFADefaultMethod = ($User.StrongAuthenticationMethods | Where-Object { $_.IsDefault -eq "True" }).MethodType
|
||||||
|
$MFAPhoneNumber = $User.StrongAuthenticationUserDetails.PhoneNumber
|
||||||
|
$PrimarySMTP = $User.ProxyAddresses | Where-Object { $_ -clike "SMTP*" } | ForEach-Object { $_ -replace "SMTP:", "" }
|
||||||
|
$Aliases = $User.ProxyAddresses | Where-Object { $_ -clike "smtp*" } | ForEach-Object { $_ -replace "smtp:", "" }
|
||||||
|
|
||||||
|
If ($User.StrongAuthenticationRequirements) {
|
||||||
|
$MFAState = $User.StrongAuthenticationRequirements.State
|
||||||
|
}
|
||||||
|
Else {
|
||||||
|
$MFAState = 'Disabled'
|
||||||
|
}
|
||||||
|
|
||||||
|
If ($MFADefaultMethod) {
|
||||||
|
Switch ($MFADefaultMethod) {
|
||||||
|
"OneWaySMS" { $MFADefaultMethod = "Text code authentication phone" }
|
||||||
|
"TwoWayVoiceMobile" { $MFADefaultMethod = "Call authentication phone" }
|
||||||
|
"TwoWayVoiceOffice" { $MFADefaultMethod = "Call office phone" }
|
||||||
|
"PhoneAppOTP" { $MFADefaultMethod = "Authenticator app or hardware token" }
|
||||||
|
"PhoneAppNotification" { $MFADefaultMethod = "Microsoft authenticator app" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Else {
|
||||||
|
$MFADefaultMethod = "Not enabled"
|
||||||
|
}
|
||||||
|
|
||||||
|
$ReportLine = [PSCustomObject] @{
|
||||||
|
UserPrincipalName = $User.UserPrincipalName
|
||||||
|
DisplayName = $User.DisplayName
|
||||||
|
MFAState = $MFAState
|
||||||
|
MFADefaultMethod = $MFADefaultMethod
|
||||||
|
MFAPhoneNumber = $MFAPhoneNumber
|
||||||
|
PrimarySMTP = ($PrimarySMTP -join ',')
|
||||||
|
Aliases = ($Aliases -join ',')
|
||||||
|
}
|
||||||
|
|
||||||
|
$Report.Add($ReportLine)
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host -Object "Processing complete."
|
||||||
|
return $Report | Select-Object UserPrincipalName, DisplayName, MFAState, MFADefaultMethod, MFAPhoneNumber, PrimarySMTP, Aliases | Sort-Object UserPrincipalName
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host -Object "You must first install MSOL using:`nInstall-Module MSOnline -Scope CurrentUser -Force"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -4,9 +4,9 @@
|
|||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
|
The Invoke-M365SecurityAudit cmdlet performs a comprehensive security audit based on the specified parameters. It allows auditing of various configurations and settings within a Microsoft 365 environment, such as compliance with CIS benchmarks.
|
||||||
.PARAMETER TenantAdminUrl
|
.PARAMETER TenantAdminUrl
|
||||||
The URL of the tenant admin. This parameter is mandatory.
|
The URL of the tenant admin. If not specified, none of the SharePoint Online tests will run.
|
||||||
.PARAMETER DomainName
|
.PARAMETER M365DomainForPWPolicyTest
|
||||||
The domain name of the Microsoft 365 environment. This parameter is mandatory.
|
The domain name of the Microsoft 365 environment to test. This parameter is not mandatory and by default it will pass/fail all found domains as a group if a specific domain is not specified.
|
||||||
.PARAMETER ELevel
|
.PARAMETER ELevel
|
||||||
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
|
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
|
||||||
.PARAMETER ProfileLevel
|
.PARAMETER ProfileLevel
|
||||||
@@ -28,71 +28,127 @@
|
|||||||
.PARAMETER NoModuleCheck
|
.PARAMETER NoModuleCheck
|
||||||
If specified, the cmdlet will not check for the presence of required modules.
|
If specified, the cmdlet will not check for the presence of required modules.
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
PS> Invoke-M365SecurityAudit
|
||||||
|
Performs a security audit using default parameters.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E3
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -ELevel "E5" -ProfileLevel "L1"
|
||||||
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E5
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -IncludeIG1
|
||||||
|
|
||||||
Performs an audit including all tests where IG1 is true.
|
Performs an audit including all tests where IG1 is true.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E3
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
|
||||||
|
|
||||||
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
|
||||||
|
Output:
|
||||||
|
Status : Fail
|
||||||
|
ELevel : E3
|
||||||
|
ProfileLevel: L1
|
||||||
|
Connection : Microsoft Graph
|
||||||
|
Rec : 1.1.1
|
||||||
|
Result : False
|
||||||
|
Details : Non-compliant accounts:
|
||||||
|
Username | Roles | HybridStatus | Missing Licence
|
||||||
|
user1@domain.com| Global Administrator | Cloud-Only | AAD_PREMIUM
|
||||||
|
user2@domain.com| Global Administrator | Hybrid | AAD_PREMIUM, AAD_PREMIUM_P2
|
||||||
|
FailureReason: Non-Compliant Accounts: 2
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -M365DomainForPWPolicyTest "contoso.com"
|
||||||
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
|
||||||
|
|
||||||
Captures the audit results into a variable and exports them to a CSV file.
|
Captures the audit results into a variable and exports them to a CSV file.
|
||||||
|
Output:
|
||||||
|
CISAuditResult[]
|
||||||
|
auditResults.csv
|
||||||
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -WhatIf
|
||||||
|
Displays what would happen if the cmdlet is run without actually performing the audit.
|
||||||
|
Output:
|
||||||
|
What if: Performing the operation "Invoke-M365SecurityAudit" on target "Microsoft 365 environment".
|
||||||
.INPUTS
|
.INPUTS
|
||||||
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
None. You cannot pipe objects to Invoke-M365SecurityAudit.
|
||||||
.OUTPUTS
|
.OUTPUTS
|
||||||
CISAuditResult[]
|
CISAuditResult[]
|
||||||
The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
|
||||||
.NOTES
|
.NOTES
|
||||||
- This module is based on CIS benchmarks.
|
- This module is based on CIS benchmarks.
|
||||||
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
|
||||||
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
|
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
|
||||||
- Modifications and sharing are allowed under the same license.
|
- Modifications and sharing are allowed under the same license.
|
||||||
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
|
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
|
||||||
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
|
- Register for CIS Benchmarks at: https://www.cisecurity.org/cis-benchmarks
|
||||||
.LINK
|
.LINK
|
||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Invoke-M365SecurityAudit
|
||||||
#>
|
#>
|
||||||
|
|
||||||
function Invoke-M365SecurityAudit {
|
function Invoke-M365SecurityAudit {
|
||||||
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
|
||||||
[OutputType([CISAuditResult[]])]
|
[OutputType([CISAuditResult[]])]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $false, HelpMessage = "The SharePoint tenant admin URL, which should end with '-admin.sharepoint.com'. If not specified none of the Sharepoint Online tests will run.")]
|
||||||
|
[ValidatePattern('^https://[a-zA-Z0-9-]+-admin\.sharepoint\.com$')]
|
||||||
[string]$TenantAdminUrl,
|
[string]$TenantAdminUrl,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $false, HelpMessage = "Specify this to test only the default domain for password expiration policy when '1.3.1' is included in the tests to be run. The domain name of your organization, e.g., 'example.com'.")]
|
||||||
[string]$DomainName,
|
[ValidatePattern('^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}$')]
|
||||||
|
[string]$M365DomainForPWPolicyTest,
|
||||||
|
|
||||||
# E-Level with optional ProfileLevel selection
|
# E-Level with optional ProfileLevel selection
|
||||||
[Parameter(ParameterSetName = 'ELevelFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
|
||||||
[ValidateSet('E3', 'E5')]
|
[ValidateSet('E3', 'E5')]
|
||||||
[string]$ELevel,
|
[string]$ELevel,
|
||||||
|
|
||||||
[Parameter(ParameterSetName = 'ELevelFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'ELevelFilter')]
|
||||||
[ValidateSet('L1', 'L2')]
|
[ValidateSet('L1', 'L2')]
|
||||||
[string]$ProfileLevel,
|
[string]$ProfileLevel,
|
||||||
|
|
||||||
# IG Filters, one at a time
|
# IG Filters, one at a time
|
||||||
[Parameter(ParameterSetName = 'IG1Filter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG1Filter')]
|
||||||
[switch]$IncludeIG1,
|
[switch]$IncludeIG1,
|
||||||
|
|
||||||
[Parameter(ParameterSetName = 'IG2Filter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG2Filter')]
|
||||||
[switch]$IncludeIG2,
|
[switch]$IncludeIG2,
|
||||||
|
|
||||||
[Parameter(ParameterSetName = 'IG3Filter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'IG3Filter')]
|
||||||
[switch]$IncludeIG3,
|
[switch]$IncludeIG3,
|
||||||
|
|
||||||
# Inclusion of specific recommendation numbers
|
# Inclusion of specific recommendation numbers
|
||||||
[Parameter(ParameterSetName = 'RecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'RecFilter')]
|
||||||
[ValidateSet(
|
[ValidateSet(
|
||||||
'1.1.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', `
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
||||||
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
||||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
@@ -103,9 +159,9 @@ function Invoke-M365SecurityAudit {
|
|||||||
[string[]]$IncludeRecommendation,
|
[string[]]$IncludeRecommendation,
|
||||||
|
|
||||||
# Exclusion of specific recommendation numbers
|
# Exclusion of specific recommendation numbers
|
||||||
[Parameter(ParameterSetName = 'SkipRecFilter')]
|
[Parameter(Mandatory = $true, ParameterSetName = 'SkipRecFilter')]
|
||||||
[ValidateSet(
|
[ValidateSet(
|
||||||
'1.1.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', `
|
'1.1.1', '1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
|
||||||
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
|
||||||
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
|
||||||
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
|
||||||
@@ -121,140 +177,117 @@ function Invoke-M365SecurityAudit {
|
|||||||
[switch]$NoModuleCheck
|
[switch]$NoModuleCheck
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Begin {
|
Begin {
|
||||||
if ($script:MaximumFunctionCount -lt 8192) {
|
if ($script:MaximumFunctionCount -lt 8192) {
|
||||||
$script:MaximumFunctionCount = 8192
|
$script:MaximumFunctionCount = 8192
|
||||||
}
|
}
|
||||||
# Ensure required modules are installed
|
# Ensure required modules are installed
|
||||||
# Define the required modules and versions in a hashtable
|
$requiredModules = Get-RequiredModule -AuditFunction
|
||||||
if (!($NoModuleCheck)) {
|
|
||||||
$requiredModules = @(
|
# Format the required modules list
|
||||||
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" },
|
$requiredModulesFormatted = Format-RequiredModuleList -RequiredModules $requiredModules
|
||||||
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" },
|
# Check and install required modules if necessary
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" },
|
if (!($NoModuleCheck) -and $PSCmdlet.ShouldProcess("Check for required modules: $requiredModulesFormatted", "Check")) {
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Groups" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "DirectoryObjects" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Domains" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Reports" },
|
|
||||||
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Mail" },
|
|
||||||
@{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000" },
|
|
||||||
@{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0" }
|
|
||||||
)
|
|
||||||
foreach ($module in $requiredModules) {
|
foreach ($module in $requiredModules) {
|
||||||
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModules $module.SubModules
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# Loop through each required module and assert its availability
|
|
||||||
|
|
||||||
# Establishing connections
|
|
||||||
#if (!($DoNotConnect -or $DoNotTest)) {
|
|
||||||
# Establishing connections
|
|
||||||
if (!($DoNotConnect)) {
|
|
||||||
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
# Load test definitions from CSV
|
# Load test definitions from CSV
|
||||||
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
|
||||||
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
# Load the Test Definitions into the script scope for use in other functions
|
# Load the Test Definitions into the script scope for use in other functions
|
||||||
$script:TestDefinitionsObject = $testDefinitions
|
$script:TestDefinitionsObject = $testDefinitions
|
||||||
|
|
||||||
# Apply filters based on parameter sets
|
# Apply filters based on parameter sets
|
||||||
switch ($PSCmdlet.ParameterSetName) {
|
$params = @{
|
||||||
'ELevelFilter' {
|
TestDefinitions = $testDefinitions
|
||||||
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
|
ParameterSetName = $PSCmdlet.ParameterSetName
|
||||||
$testDefinitions = $testDefinitions | Where-Object {
|
ELevel = $ELevel
|
||||||
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
|
ProfileLevel = $ProfileLevel
|
||||||
}
|
IncludeRecommendation = $IncludeRecommendation
|
||||||
|
SkipRecommendation = $SkipRecommendation
|
||||||
|
}
|
||||||
|
$testDefinitions = Get-TestDefinitionsObject @params
|
||||||
|
# Extract unique connections needed
|
||||||
|
$requiredConnections = $testDefinitions.Connection | Sort-Object -Unique
|
||||||
|
if ($requiredConnections -contains 'SPO') {
|
||||||
|
if (-not $TenantAdminUrl) {
|
||||||
|
$requiredConnections = $requiredConnections | Where-Object { $_ -ne 'SPO' }
|
||||||
|
$testDefinitions = $testDefinitions | Where-Object { $_.Connection -ne 'SPO' }
|
||||||
|
if ($null -eq $testDefinitions) {
|
||||||
|
throw "No tests to run as no SharePoint Online tests are available."
|
||||||
}
|
}
|
||||||
elseif ($null -ne $ELevel) {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object {
|
|
||||||
$_.ELevel -eq $ELevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elseif ($null -ne $ProfileLevel) {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object {
|
|
||||||
$_.ProfileLevel -eq $ProfileLevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'IG1Filter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
|
|
||||||
}
|
|
||||||
'IG2Filter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
|
|
||||||
}
|
|
||||||
'IG3Filter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
|
|
||||||
}
|
|
||||||
'RecFilter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
|
|
||||||
}
|
|
||||||
'SkipRecFilter' {
|
|
||||||
$testDefinitions = $testDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# End switch ($PSCmdlet.ParameterSetName)
|
|
||||||
|
|
||||||
# Determine which test files to load based on filtering
|
# Determine which test files to load based on filtering
|
||||||
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
$testsToLoad = $testDefinitions.TestFileName | ForEach-Object { $_ -replace '.ps1$', '' }
|
||||||
|
|
||||||
# Display the tests that would be loaded if the function is called with -WhatIf
|
|
||||||
|
|
||||||
Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:"
|
Write-Verbose "The $(($testsToLoad).count) test/s that would be loaded based on filter criteria:"
|
||||||
$testsToLoad | ForEach-Object { Write-Verbose " $_" }
|
$testsToLoad | ForEach-Object { Write-Verbose " $_" }
|
||||||
|
# Initialize a collection to hold failed test details
|
||||||
|
$script:FailedTests = [System.Collections.ArrayList]::new()
|
||||||
} # End Begin
|
} # End Begin
|
||||||
|
|
||||||
Process {
|
Process {
|
||||||
$allAuditResults = [System.Collections.ArrayList]::new() #@() # Initialize a collection to hold all results
|
$allAuditResults = [System.Collections.ArrayList]::new() # Initialize a collection to hold all results
|
||||||
|
|
||||||
# Dynamically dot-source the test scripts
|
# Dynamically dot-source the test scripts
|
||||||
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
|
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
|
||||||
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
|
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
|
||||||
Where-Object { $testsToLoad -contains $_.BaseName }
|
Where-Object { $testsToLoad -contains $_.BaseName }
|
||||||
|
|
||||||
|
$totalTests = $testFiles.Count
|
||||||
|
$currentTestIndex = 0
|
||||||
|
|
||||||
|
# Establishing connections if required
|
||||||
|
$actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections
|
||||||
|
if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) {
|
||||||
|
Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" -InformationAction Continue
|
||||||
|
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Write-Information "A total of $($totalTests) tests were selected to run..." -InformationAction Continue
|
||||||
# Import the test functions
|
# Import the test functions
|
||||||
$testFiles | ForEach-Object {
|
$testFiles | ForEach-Object {
|
||||||
|
$currentTestIndex++
|
||||||
|
Write-Progress -Activity "Loading Test Scripts" -Status "Loading $($currentTestIndex) of $($totalTests): $($_.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
||||||
Try {
|
Try {
|
||||||
|
# Dot source the test function
|
||||||
. $_.FullName
|
. $_.FullName
|
||||||
}
|
}
|
||||||
Catch {
|
Catch {
|
||||||
|
# Log the error and add the test to the failed tests collection
|
||||||
Write-Error "Failed to load test function $($_.Name): $_"
|
Write-Error "Failed to load test function $($_.Name): $_"
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Test = $_.Name; Error = $_ })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$currentTestIndex = 0
|
||||||
# Execute each test function from the prepared list
|
# Execute each test function from the prepared list
|
||||||
foreach ($testFunction in $testFiles) {
|
foreach ($testFunction in $testFiles) {
|
||||||
|
$currentTestIndex++
|
||||||
|
Write-Progress -Activity "Executing Tests" -Status "Executing $($currentTestIndex) of $($totalTests): $($testFunction.Name)" -PercentComplete (($currentTestIndex / $totalTests) * 100)
|
||||||
$functionName = $testFunction.BaseName
|
$functionName = $testFunction.BaseName
|
||||||
$functionCmd = Get-Command -Name $functionName
|
|
||||||
|
|
||||||
# Check if the test function needs DomainName parameter
|
|
||||||
$paramList = @{}
|
|
||||||
if ('DomainName' -in $functionCmd.Parameters.Keys) {
|
|
||||||
$paramList.DomainName = $DomainName
|
|
||||||
}
|
|
||||||
|
|
||||||
# Use splatting to pass parameters
|
|
||||||
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
|
if ($PSCmdlet.ShouldProcess($functionName, "Execute test")) {
|
||||||
Write-Host "Running $functionName..."
|
$auditResult = Invoke-TestFunction -FunctionFile $testFunction -DomainName $M365DomainForPWPolicyTest
|
||||||
$result = & $functionName @paramList
|
# Add the result to the collection
|
||||||
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
|
[void]$allAuditResults.Add($auditResult)
|
||||||
[void]($allAuditResults.add($Result))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
End {
|
End {
|
||||||
if (!($DoNotDisconnect)) {
|
if (!($DoNotDisconnect) -and $PSCmdlet.ShouldProcess("Disconnect from Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Disconnect")) {
|
||||||
# Clean up sessions
|
# Clean up sessions
|
||||||
Disconnect-M365Suite
|
Disconnect-M365Suite -RequiredConnections $requiredConnections
|
||||||
|
}
|
||||||
|
if ($PSCmdlet.ShouldProcess("Measure and display audit results for $($totalTests) tests", "Measure")) {
|
||||||
|
# Call the private function to calculate and display results
|
||||||
|
Measure-AuditResult -AllAuditResults $allAuditResults -FailedTests $script:FailedTests
|
||||||
|
# Return all collected audit results
|
||||||
|
return $allAuditResults.ToArray() | Sort-Object -Property Rec
|
||||||
}
|
}
|
||||||
# Return all collected audit results
|
|
||||||
return $allAuditResults.ToArray()
|
|
||||||
# Check if the Disconnect switch is present
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,54 +1,83 @@
|
|||||||
<#
|
<#
|
||||||
.SYNOPSIS
|
.SYNOPSIS
|
||||||
Synchronizes data between an Excel file and a CSV file and optionally updates the Excel worksheet.
|
Synchronizes data between an Excel file and either a CSV file or an output object from Invoke-M365SecurityAudit, and optionally updates the Excel worksheet.
|
||||||
.DESCRIPTION
|
.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.
|
The Sync-CISExcelAndCsvData function merges data from a specified Excel file with data from either a CSV file or an output object from Invoke-M365SecurityAudit based on a common key. It can also update the Excel worksheet with the merged data. This function is particularly useful for updating Excel records with additional data from a CSV file or audit results while preserving the original formatting and structure of the Excel worksheet.
|
||||||
.PARAMETER ExcelPath
|
.PARAMETER ExcelPath
|
||||||
The path to the Excel file that contains the original data. This parameter is mandatory.
|
The path to the Excel file that contains the original data. This parameter is mandatory.
|
||||||
.PARAMETER WorksheetName
|
.PARAMETER WorksheetName
|
||||||
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
|
The name of the worksheet within the Excel file that contains the data to be synchronized. This parameter is mandatory.
|
||||||
.PARAMETER CsvPath
|
.PARAMETER CsvPath
|
||||||
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory.
|
The path to the CSV file containing data to be merged with the Excel data. This parameter is mandatory when using the CsvInput parameter set.
|
||||||
.PARAMETER SkipUpdate
|
.PARAMETER AuditResults
|
||||||
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
|
An array of CISAuditResult objects from Invoke-M365SecurityAudit to be merged with the Excel data. This parameter is mandatory when using the ObjectInput parameter set. It can also accept pipeline input.
|
||||||
.EXAMPLE
|
.PARAMETER SkipUpdate
|
||||||
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv"
|
If specified, the function will return the merged data object without updating the Excel worksheet. This is useful for previewing the merged data.
|
||||||
Merges data from 'data.csv' into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
.EXAMPLE
|
||||||
.EXAMPLE
|
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv"
|
||||||
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
|
Merges data from 'data.csv' into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||||
Retrieves the merged data object for preview without updating the Excel worksheet.
|
.EXAMPLE
|
||||||
.INPUTS
|
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -CsvPath "path\to\data.csv" -SkipUpdate
|
||||||
None. You cannot pipe objects to Sync-CISExcelAndCsvData.
|
Retrieves the merged data object for preview without updating the Excel worksheet.
|
||||||
.OUTPUTS
|
.EXAMPLE
|
||||||
Object[]
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
|
||||||
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
|
PS> Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults
|
||||||
.NOTES
|
Merges data from the audit results into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||||
- Ensure that the 'ImportExcel' module is installed and up to date.
|
.EXAMPLE
|
||||||
- It is recommended to backup the Excel file before running this script to prevent accidental data loss.
|
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com"
|
||||||
- This function is part of the CIS Excel and CSV Data Management Toolkit.
|
PS> $mergedData = Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet" -AuditResults $auditResults -SkipUpdate
|
||||||
.LINK
|
Retrieves the merged data object for preview without updating the Excel worksheet.
|
||||||
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
.EXAMPLE
|
||||||
|
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://tenant-admin.url" -DomainName "example.com" | Sync-CISExcelAndCsvData -ExcelPath "path\to\excel.xlsx" -WorksheetName "DataSheet"
|
||||||
|
Pipes the audit results into Sync-CISExcelAndCsvData to merge data into 'excel.xlsx' on the 'DataSheet' worksheet and updates the worksheet with the merged data.
|
||||||
|
.INPUTS
|
||||||
|
System.String, CISAuditResult[]
|
||||||
|
You can pipe CISAuditResult objects to Sync-CISExcelAndCsvData.
|
||||||
|
.OUTPUTS
|
||||||
|
Object[]
|
||||||
|
If the SkipUpdate switch is used, the function returns an array of custom objects representing the merged data.
|
||||||
|
.NOTES
|
||||||
|
- Ensure that the 'ImportExcel' module is installed and up to date.
|
||||||
|
- It is recommended to backup the Excel file before running this script to prevent accidental data loss.
|
||||||
|
- This function is part of the CIS Excel and CSV Data Management Toolkit.
|
||||||
|
.LINK
|
||||||
|
https://criticalsolutionsnetwork.github.io/M365FoundationsCISReport/#Sync-CISExcelAndCsvData
|
||||||
#>
|
#>
|
||||||
|
|
||||||
function Sync-CISExcelAndCsvData {
|
function Sync-CISExcelAndCsvData {
|
||||||
[CmdletBinding()]
|
[OutputType([void], [PSCustomObject[]])]
|
||||||
|
[CmdletBinding(DefaultParameterSetName = 'CsvInput')]
|
||||||
param (
|
param (
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
|
[ValidateScript({ Test-Path $_ })]
|
||||||
[string]$ExcelPath,
|
[string]$ExcelPath,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true)]
|
||||||
[string]$WorksheetName,
|
[string]$WorksheetName,
|
||||||
|
|
||||||
[Parameter(Mandatory = $true)]
|
[Parameter(Mandatory = $true, ParameterSetName = 'CsvInput')]
|
||||||
|
[ValidateScript({ Test-Path $_ })]
|
||||||
[string]$CsvPath,
|
[string]$CsvPath,
|
||||||
|
|
||||||
|
[Parameter(Mandatory = $true, ParameterSetName = 'ObjectInput', ValueFromPipeline = $true)]
|
||||||
|
[CISAuditResult[]]$AuditResults,
|
||||||
|
|
||||||
[Parameter(Mandatory = $false)]
|
[Parameter(Mandatory = $false)]
|
||||||
[switch]$SkipUpdate
|
[switch]$SkipUpdate
|
||||||
)
|
)
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# Merge Excel and CSV data
|
# Verify ImportExcel module is available
|
||||||
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
|
$requiredModules = Get-RequiredModule -SyncFunction
|
||||||
|
foreach ($module in $requiredModules) {
|
||||||
|
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
|
||||||
|
}
|
||||||
|
|
||||||
|
# Merge Excel and CSV data or Audit Results
|
||||||
|
if ($PSCmdlet.ParameterSetName -eq 'CsvInput') {
|
||||||
|
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -CsvPath $CsvPath
|
||||||
|
} else {
|
||||||
|
$mergedData = Merge-CISExcelAndCsvData -ExcelPath $ExcelPath -WorksheetName $WorksheetName -AuditResults $AuditResults
|
||||||
|
}
|
||||||
|
|
||||||
# Output the merged data if the user chooses to skip the update
|
# Output the merged data if the user chooses to skip the update
|
||||||
if ($SkipUpdate) {
|
if ($SkipUpdate) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
Index,TestFileName,Rec,RecDescription,ELevel,ProfileLevel,CISControl,CISDescription,IG1,IG2,IG3,Automated,Connection
|
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,AzureAD
|
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
|
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
|
3,Test-ManagedApprovedPublicGroups.ps1,1.2.1,Ensure that only organizationally managed/approved public groups exist,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Graph
|
||||||
4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,AzureAD | EXO
|
4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,AzureAD | EXO
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
||||||
@@ -18,8 +18,8 @@
|
|||||||
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
|
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
|
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
|
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,EXO
|
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,EXO | Microsoft Graph
|
||||||
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,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,EXO | Microsoft Graph
|
||||||
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
|
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
|
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
|
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
|
||||||
@@ -28,14 +28,14 @@
|
|||||||
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
|
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
|
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
|
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
|
30,Test-SharePointAADB2B.ps1,7.2.2,Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled,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
|
31,Test-RestrictExternalSharing.ps1,7.2.3,Ensure external content sharing is restricted,E3,L1,3.3,Configure Data Access Control Lists,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
|
32,Test-OneDriveContentRestrictions.ps1,7.2.4,Ensure OneDrive 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
|
33,Test-SharePointGuestsItemSharing.ps1,7.2.5,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
|
||||||
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
|
34,Test-SharePointExternalSharingDomains.ps1,7.2.6,Ensure SharePoint external sharing is managed through domain whitelist/blacklists,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
|
35,Test-LinkSharingRestrictions.ps1,7.2.7,Ensure link sharing is restricted in SharePoint and OneDrive,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
|
36,Test-GuestAccessExpiration.ps1,7.2.9,Ensure guest access to a site or OneDrive will expire automatically,E3,L1,0,Explicitly Not Mapped,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
|
37,Test-ReauthWithCode.ps1,7.2.10,Ensure reauthentication with verification code is restricted,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
|
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
|
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
|
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
|
||||||
|
|
@@ -6,74 +6,127 @@ function Test-AdministrativeAccountCompliance {
|
|||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
# The following conditions are checked:
|
||||||
|
# Condition A: The administrative account is cloud-only (not synced).
|
||||||
|
# Condition B: The account is assigned a valid license (e.g., Microsoft Entra ID P1 or P2).
|
||||||
|
# Condition C: The administrative account does not have any other application assignments (only valid licenses).
|
||||||
|
|
||||||
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
|
$validLicenses = @('AAD_PREMIUM', 'AAD_PREMIUM_P2')
|
||||||
|
$recnum = "1.1.1"
|
||||||
|
Write-Verbose "Starting Test-AdministrativeAccountCompliance with Rec: $recnum"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
|
try {
|
||||||
$adminRoleUsers = @()
|
# Retrieve all admin roles
|
||||||
|
Write-Verbose "Retrieving all admin roles"
|
||||||
|
$adminRoles = Get-MgRoleManagementDirectoryRoleDefinition | Where-Object { $_.DisplayName -like "*Admin*" }
|
||||||
|
$adminRoleUsers = @()
|
||||||
|
|
||||||
foreach ($role in $adminRoles) {
|
# Loop through each admin role to get role assignments and user details
|
||||||
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
|
foreach ($role in $adminRoles) {
|
||||||
|
Write-Verbose "Processing role: $($role.DisplayName)"
|
||||||
|
$roleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "roleDefinitionId eq '$($role.Id)'"
|
||||||
|
|
||||||
foreach ($assignment in $roleAssignments) {
|
foreach ($assignment in $roleAssignments) {
|
||||||
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
|
Write-Verbose "Processing role assignment for principal ID: $($assignment.PrincipalId)"
|
||||||
if ($userDetails) {
|
# Get user details for each principal ID
|
||||||
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
|
$userDetails = Get-MgUser -UserId $assignment.PrincipalId -Property "DisplayName, UserPrincipalName, Id, OnPremisesSyncEnabled" -ErrorAction SilentlyContinue
|
||||||
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
|
if ($userDetails) {
|
||||||
|
Write-Verbose "Retrieved user details for: $($userDetails.UserPrincipalName)"
|
||||||
|
# Get user license details
|
||||||
|
$licenses = Get-MgUserLicenseDetail -UserId $assignment.PrincipalId -ErrorAction SilentlyContinue
|
||||||
|
$licenseString = if ($licenses) { ($licenses.SkuPartNumber -join '|') } else { "No Licenses Found" }
|
||||||
|
|
||||||
$adminRoleUsers += [PSCustomObject]@{
|
# Condition A: Check if the account is cloud-only
|
||||||
UserName = $userDetails.UserPrincipalName
|
$cloudOnlyStatus = if ($userDetails.OnPremisesSyncEnabled) { "Fail" } else { "Pass" }
|
||||||
RoleName = $role.DisplayName
|
|
||||||
UserId = $userDetails.Id
|
# Condition B: Check if the account has valid licenses
|
||||||
HybridUser = $userDetails.OnPremisesSyncEnabled
|
$hasValidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -contains $_ }
|
||||||
Licenses = $licenseString
|
$validLicensesStatus = if ($hasValidLicense) { "Pass" } else { "Fail" }
|
||||||
|
|
||||||
|
# Condition C: Check if the account has no other licenses
|
||||||
|
$hasInvalidLicense = $licenses.SkuPartNumber | ForEach-Object { $validLicenses -notcontains $_ }
|
||||||
|
$applicationAssignmentStatus = if ($hasInvalidLicense) { "Fail" } else { "Pass" }
|
||||||
|
|
||||||
|
Write-Verbose "User: $($userDetails.UserPrincipalName), Cloud-Only: $cloudOnlyStatus, Valid Licenses: $validLicensesStatus, Other Applications Assigned: $applicationAssignmentStatus"
|
||||||
|
|
||||||
|
# Collect user information
|
||||||
|
$adminRoleUsers += [PSCustomObject]@{
|
||||||
|
UserName = $userDetails.UserPrincipalName
|
||||||
|
RoleName = $role.DisplayName
|
||||||
|
UserId = $userDetails.Id
|
||||||
|
HybridUser = $userDetails.OnPremisesSyncEnabled
|
||||||
|
Licenses = $licenseString
|
||||||
|
CloudOnlyStatus = $cloudOnlyStatus
|
||||||
|
ValidLicensesStatus = $validLicensesStatus
|
||||||
|
ApplicationAssignmentStatus = $applicationAssignmentStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Verbose "No user details found for principal ID: $($assignment.PrincipalId)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$uniqueAdminRoleUsers = $adminRoleUsers | Group-Object -Property UserName | ForEach-Object {
|
# Group admin role users by UserName and collect unique roles and licenses
|
||||||
$first = $_.Group | Select-Object -First 1
|
Write-Verbose "Grouping admin role users by UserName"
|
||||||
$roles = ($_.Group.RoleName -join ', ')
|
$uniqueAdminRoleUsers = $adminRoleUsers | Group-Object -Property UserName | ForEach-Object {
|
||||||
$licenses = (($_.Group | Select-Object -ExpandProperty Licenses) -join ',').Split(',') | Select-Object -Unique
|
$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 '|' } }
|
$first | Select-Object UserName, UserId, HybridUser, @{Name = 'Roles'; Expression = { $roles } }, @{Name = 'Licenses'; Expression = { $licenses -join '|' } }, CloudOnlyStatus, ValidLicensesStatus, ApplicationAssignmentStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
$nonCompliantUsers = $uniqueAdminRoleUsers | Where-Object {
|
# Identify non-compliant users based on conditions A, B, and C
|
||||||
$_.HybridUser -or
|
Write-Verbose "Identifying non-compliant users based on conditions"
|
||||||
-not ($_.Licenses -split '\|' | Where-Object { $validLicenses -contains $_ })
|
$nonCompliantUsers = $uniqueAdminRoleUsers | Where-Object {
|
||||||
}
|
$_.HybridUser -or # Fails Condition A
|
||||||
|
$_.ValidLicensesStatus -eq "Fail" -or # Fails Condition B
|
||||||
|
$_.ApplicationAssignmentStatus -eq "Fail" # Fails Condition C
|
||||||
|
}
|
||||||
|
|
||||||
$failureReasons = $nonCompliantUsers | ForEach-Object {
|
# Generate failure reasons
|
||||||
$accountType = if ($_.HybridUser) { "Hybrid" } else { "Cloud-Only" }
|
Write-Verbose "Generating failure reasons for non-compliant users"
|
||||||
$missingLicenses = $validLicenses | Where-Object { $_ -notin ($_.Licenses -split '\|') }
|
$failureReasons = $nonCompliantUsers | ForEach-Object {
|
||||||
"$($_.UserName)|$($_.Roles)|$accountType|Missing: $($missingLicenses -join ',')"
|
"$($_.UserName)|$($_.Roles)|$($_.CloudOnlyStatus)|$($_.ValidLicensesStatus)|$($_.ApplicationAssignmentStatus)"
|
||||||
}
|
}
|
||||||
$failureReasons = $failureReasons -join "`n"
|
$failureReasons = $failureReasons -join "`n"
|
||||||
$details = if ($nonCompliantUsers) {
|
$failureReason = if ($nonCompliantUsers) {
|
||||||
"Non-Compliant Accounts: $($nonCompliantUsers.Count)`nDetails:`n" + ($nonCompliantUsers | ForEach-Object { $_.UserName }) -join "`n"
|
"Non-Compliant Accounts: $($nonCompliantUsers.Count)"
|
||||||
}
|
} else {
|
||||||
else {
|
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
|
||||||
"Compliant Accounts: $($uniqueAdminRoleUsers.Count)"
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$result = $nonCompliantUsers.Count -eq 0
|
$result = $nonCompliantUsers.Count -eq 0
|
||||||
$status = if ($result) { 'Pass' } else { 'Fail' }
|
$status = if ($result) { 'Pass' } else { 'Fail' }
|
||||||
$failureReason = if ($nonCompliantUsers) { "Non-compliant accounts: `nUsername | Roles | HybridStatus | Missing Licence`n$failureReasons" } else { "N/A" }
|
$details = if ($nonCompliantUsers) { "Non-compliant accounts: `nUsername | Roles | Cloud-Only Status | Entra ID License Status | Other Applications Assigned Status`n$failureReasons" } else { "N/A" }
|
||||||
|
|
||||||
# Create the parameter splat
|
Write-Verbose "Assessment completed. Result: $status"
|
||||||
$params = @{
|
|
||||||
Rec = "1.1.1"
|
# Create the parameter splat
|
||||||
Result = $result
|
$params = @{
|
||||||
Status = $status
|
Rec = $recnum
|
||||||
Details = $details
|
Result = $result
|
||||||
FailureReason = $failureReason
|
Status = $status
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReason
|
||||||
|
}
|
||||||
|
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
# Handle the error and create a failure result
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-AntiPhishingPolicy {
|
function Test-AntiPhishingPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -10,66 +11,98 @@ function Test-AntiPhishingPolicy {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
#$auditResults = @()
|
#$auditResults = @()
|
||||||
|
$recnum = "2.1.7"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 2.1.7 (L1) Ensure that an anti-phishing policy has been created
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Verify that an anti-phishing policy exists in the Microsoft 365 Security Center.
|
||||||
|
- Condition B: Using PowerShell, ensure the anti-phishing policy is configured with appropriate settings such as enabling impersonation protection and spoof intelligence.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: No anti-phishing policy exists in the Microsoft 365 Security Center.
|
||||||
|
- Condition B: Using PowerShell, the anti-phishing policy is not configured with the required settings.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.7 Ensure that an anti-phishing policy has been created
|
|
||||||
|
|
||||||
# Retrieve and validate the anti-phishing policies
|
try {
|
||||||
$antiPhishPolicies = Get-AntiPhishPolicy
|
# Condition A: Ensure that an anti-phishing policy has been created
|
||||||
$validatedPolicies = $antiPhishPolicies | Where-Object {
|
$antiPhishPolicies = Get-AntiPhishPolicy
|
||||||
$_.Enabled -eq $true -and
|
|
||||||
$_.PhishThresholdLevel -ge 2 -and
|
|
||||||
$_.EnableMailboxIntelligenceProtection -eq $true -and
|
|
||||||
$_.EnableMailboxIntelligence -eq $true -and
|
|
||||||
$_.EnableSpoofIntelligence -eq $true
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if there is at least one policy that meets the requirements
|
# Condition B: Verify the anti-phishing policy settings using PowerShell
|
||||||
$nonCompliantItems = $antiPhishPolicies | Where-Object {
|
$validatedPolicies = $antiPhishPolicies | Where-Object {
|
||||||
$_.Enabled -ne $true -or
|
$_.Enabled -eq $true -and
|
||||||
$_.PhishThresholdLevel -lt 2 -or
|
$_.PhishThresholdLevel -ge 2 -and
|
||||||
$_.EnableMailboxIntelligenceProtection -ne $true -or
|
$_.EnableMailboxIntelligenceProtection -eq $true -and
|
||||||
$_.EnableMailboxIntelligence -ne $true -or
|
$_.EnableMailboxIntelligence -eq $true -and
|
||||||
$_.EnableSpoofIntelligence -ne $true
|
$_.EnableSpoofIntelligence -eq $true
|
||||||
}
|
}
|
||||||
$compliantItems = $validatedPolicies
|
|
||||||
$isCompliant = $compliantItems.Count -gt 0
|
|
||||||
|
|
||||||
# Prepare failure reasons for non-compliant items
|
# Check if there is at least one policy that meets the requirements
|
||||||
$nonCompliantNames = $nonCompliantItems | ForEach-Object { $_.Name }
|
$nonCompliantItems = $antiPhishPolicies | Where-Object {
|
||||||
$failureReasons = if ($nonCompliantNames.Count -gt 0) {
|
$_.Enabled -ne $true -or
|
||||||
"Reason: Does not meet one or more compliance criteria.`nNon-compliant Policies:`n" + ($nonCompliantNames -join "`n")
|
$_.PhishThresholdLevel -lt 2 -or
|
||||||
}
|
$_.EnableMailboxIntelligenceProtection -ne $true -or
|
||||||
else {
|
$_.EnableMailboxIntelligence -ne $true -or
|
||||||
"N/A"
|
$_.EnableSpoofIntelligence -ne $true
|
||||||
}
|
}
|
||||||
|
$compliantItems = $validatedPolicies
|
||||||
|
$isCompliant = $compliantItems.Count -gt 0
|
||||||
|
|
||||||
# Prepare details for non-compliant items
|
# Prepare failure reasons for non-compliant items
|
||||||
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
|
$nonCompliantNames = $nonCompliantItems | ForEach-Object { $_.Name }
|
||||||
"Policy: $($_.Name)"
|
$failureReasons = if ($nonCompliantNames.Count -gt 0) {
|
||||||
}
|
"Reason: Does not meet one or more compliance criteria.`nNon-compliant Policies:`n" + ($nonCompliantNames -join "`n")
|
||||||
$nonCompliantDetails = $nonCompliantDetails -join "`n"
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Prepare details based on compliance
|
# Prepare details for non-compliant items
|
||||||
$details = if ($nonCompliantItems) {
|
$nonCompliantDetails = $nonCompliantItems | ForEach-Object {
|
||||||
"Non-Compliant Items: $($nonCompliantItems.Count)`nDetails:`n$nonCompliantDetails"
|
"Policy: $($_.Name)"
|
||||||
}
|
}
|
||||||
else {
|
$nonCompliantDetails = $nonCompliantDetails -join "`n"
|
||||||
"Compliant Items: $($compliantItems.Count)"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Parameter splat for Initialize-CISAuditResult function
|
# Prepare details based on compliance
|
||||||
$params = @{
|
$details = if ($nonCompliantItems) {
|
||||||
Rec = "2.1.7"
|
"Non-Compliant Items: $($nonCompliantItems.Count)`nDetails:`n$nonCompliantDetails"
|
||||||
Result = $nonCompliantItems.Count -eq 0
|
}
|
||||||
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
else {
|
||||||
Details = $details
|
"Compliant Items: $($compliantItems.Count)"
|
||||||
FailureReason = $failureReasons
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Parameter splat for Initialize-CISAuditResult function
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $nonCompliantItems.Count -eq 0
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-AuditDisabledFalse {
|
function Test-AuditDisabledFalse {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
# Aligned
|
# Aligned
|
||||||
param (
|
param (
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -8,40 +9,72 @@ function Test-AuditDisabledFalse {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
|
# Conditions for 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AuditDisabled` organizational setting is set to `False` in the Microsoft 365 admin center.
|
||||||
|
# - Condition B: Using PowerShell, the `AuditDisabled` property in the organization's configuration is set to `False`.
|
||||||
|
# - Condition C: Ensure mailbox auditing is enabled by default at the organizational level.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AuditDisabled` organizational setting is set to `True` in the Microsoft 365 admin center.
|
||||||
|
# - Condition B: Using PowerShell, the `AuditDisabled` property in the organization's configuration is set to `True`.
|
||||||
|
# - Condition C: Mailbox auditing is not enabled by default at the organizational level.
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.1.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
try {
|
||||||
|
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
|
||||||
|
|
||||||
# Retrieve the AuditDisabled configuration
|
# Retrieve the AuditDisabled configuration (Condition B)
|
||||||
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
|
||||||
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $auditNotDisabled) {
|
$failureReasons = if (-not $auditNotDisabled) {
|
||||||
"AuditDisabled is set to True"
|
"AuditDisabled is set to True" # Condition A Fail
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = if ($auditNotDisabled) {
|
$details = if ($auditNotDisabled) {
|
||||||
"Audit is not disabled organizationally"
|
"Audit is not disabled organizationally" # Condition C Pass
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"Audit is disabled organizationally"
|
"Audit is disabled organizationally" # Condition C Fail
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "6.1.1"
|
Rec = $recnum
|
||||||
Result = $auditNotDisabled
|
Result = $auditNotDisabled
|
||||||
Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-AuditLogSearch {
|
function Test-AuditLogSearch {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -9,40 +10,75 @@ function Test-AuditLogSearch {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "3.1.1"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Audit log search is enabled in the Microsoft Purview compliance portal.
|
||||||
|
- Condition B: The audit log retains user and admin activity for 90 days.
|
||||||
|
- Condition C: Audit log search capabilities are functional (search results are displayed for activities within the past 30 days).
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Audit log search is not enabled in the Microsoft Purview compliance portal.
|
||||||
|
- Condition B: The audit log does not retain user and admin activity for 90 days.
|
||||||
|
- Condition C: Audit log search capabilities are non-functional (no search results are displayed for activities within the past 30 days).
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
|
||||||
|
|
||||||
# Retrieve the audit log configuration
|
try {
|
||||||
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
|
||||||
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve the audit log configuration
|
||||||
$failureReasons = if (-not $auditLogResult) {
|
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
|
||||||
"Audit log search is not enabled"
|
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($auditLogResult) {
|
# Prepare failure reasons and details based on compliance
|
||||||
"UnifiedAuditLogIngestionEnabled: True"
|
$failureReasons = if (-not $auditLogResult) {
|
||||||
}
|
# Condition A (Fail): Audit log search is not enabled in the Microsoft Purview compliance portal
|
||||||
else {
|
"Audit log search is not enabled"
|
||||||
"UnifiedAuditLogIngestionEnabled: False"
|
}
|
||||||
}
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$details = if ($auditLogResult) {
|
||||||
$params = @{
|
# Condition A (Pass): Audit log search is enabled in the Microsoft Purview compliance portal
|
||||||
Rec = "3.1.1"
|
"UnifiedAuditLogIngestionEnabled: True"
|
||||||
Result = $auditLogResult
|
}
|
||||||
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
else {
|
||||||
Details = $details
|
"UnifiedAuditLogIngestionEnabled: False"
|
||||||
FailureReason = $failureReasons
|
}
|
||||||
}
|
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $auditLogResult
|
||||||
|
Status = if ($auditLogResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-BlockChannelEmails {
|
function Test-BlockChannelEmails {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added here if needed
|
# Parameters can be added here if needed
|
||||||
@@ -9,39 +10,69 @@ function Test-BlockChannelEmails {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
|
||||||
|
|
||||||
# Retrieve Teams client configuration
|
try {
|
||||||
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
|
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
|
||||||
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowEmailIntoChannel` setting in Teams is set to `False`.
|
||||||
|
# - Condition B: The setting `Users can send emails to a channel email address` is set to `Off` in the Teams admin center.
|
||||||
|
# - Condition C: Verification using PowerShell confirms that the `AllowEmailIntoChannel` setting is disabled.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowEmailIntoChannel` setting in Teams is not set to `False`.
|
||||||
|
# - Condition B: The setting `Users can send emails to a channel email address` is not set to `Off` in the Teams admin center.
|
||||||
|
# - Condition C: Verification using PowerShell indicates that the `AllowEmailIntoChannel` setting is enabled.
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve Teams client configuration
|
||||||
$failureReasons = if ($allowEmailIntoChannel) {
|
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
|
||||||
"Emails can be sent to a channel email address"
|
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($allowEmailIntoChannel) {
|
# Prepare failure reasons and details based on compliance
|
||||||
"AllowEmailIntoChannel is set to True"
|
$failureReasons = if ($allowEmailIntoChannel) {
|
||||||
}
|
"Emails can be sent to a channel email address" # Condition A Fail: AllowEmailIntoChannel is True
|
||||||
else {
|
}
|
||||||
"AllowEmailIntoChannel is set to False"
|
else {
|
||||||
}
|
"N/A" # Condition A Pass: AllowEmailIntoChannel is False
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$details = if ($allowEmailIntoChannel) {
|
||||||
$params = @{
|
"AllowEmailIntoChannel is set to True" # Condition B Fail: Emails are allowed
|
||||||
Rec = "8.1.2"
|
}
|
||||||
Result = -not $allowEmailIntoChannel
|
else {
|
||||||
Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
|
"AllowEmailIntoChannel is set to False" # Condition B Pass: Emails are blocked
|
||||||
Details = $details
|
}
|
||||||
FailureReason = $failureReasons
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = -not $allowEmailIntoChannel
|
||||||
|
Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
function Test-BlockMailForwarding {
|
function Test-BlockMailForwarding {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned Compare
|
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -9,40 +9,98 @@ function Test-BlockMailForwarding {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.2.1"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Transport rules do not forward email to external domains.
|
||||||
|
- Condition B: Anti-spam outbound policy is configured to disable automatic email forwarding to external domains.
|
||||||
|
- Condition C: No exceptions to the forwarding rules unless explicitly defined by organizational policy.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: One or more transport rules forward email to external domains.
|
||||||
|
- Condition B: Anti-spam outbound policy does not disable automatic email forwarding to external domains.
|
||||||
|
- Condition C: Unapproved exceptions to the forwarding rules are present.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
try {
|
||||||
|
# 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled
|
||||||
|
|
||||||
# Retrieve the transport rules that redirect messages
|
# Step 1: Retrieve the transport rules that redirect messages
|
||||||
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
$transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo }
|
||||||
$forwardingBlocked = $transportRules.Count -eq 0
|
$transportForwardingBlocked = $transportRules.Count -eq 0
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step 2: Check all anti-spam outbound policies
|
||||||
$failureReasons = if ($transportRules.Count -gt 0) {
|
$outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy
|
||||||
"Mail forwarding rules found: $($transportRules.Name -join ', ')"
|
$nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' }
|
||||||
}
|
$nonCompliantSpamPoliciesArray = @($nonCompliantSpamPolicies)
|
||||||
else {
|
$spamForwardingBlocked = $nonCompliantSpamPoliciesArray.Count -eq 0
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($transportRules.Count -gt 0) {
|
# Determine overall compliance
|
||||||
$transportRules | ForEach-Object {
|
$forwardingBlocked = $transportForwardingBlocked -and $spamForwardingBlocked
|
||||||
"$($_.Name) redirects to $($_.RedirectMessageTo)"
|
|
||||||
} -join " | "
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark."
|
|
||||||
}
|
|
||||||
|
|
||||||
$params = @{
|
# Prepare failure reasons and details based on compliance
|
||||||
Rec = "6.2.1"
|
$failureReasons = @()
|
||||||
Result = $forwardingBlocked
|
$details = @()
|
||||||
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
if ($transportRules.Count -gt 0) {
|
||||||
FailureReason = $failureReasons
|
# Fail Condition A
|
||||||
|
$failureReasons += "Mail forwarding rules found: $($transportRules.Name -join ', ')"
|
||||||
|
$details += "Transport Rules Details:`nRule Name|Redirects To"
|
||||||
|
$details += $transportRules | ForEach-Object {
|
||||||
|
"$($_.Name)|$($_.RedirectMessageTo -join ', ')"
|
||||||
|
}
|
||||||
|
$details += "`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($nonCompliantSpamPoliciesArray.Count -gt 0) {
|
||||||
|
# Fail Condition B
|
||||||
|
$failureReasons += "Outbound spam policies allowing automatic forwarding found."
|
||||||
|
$details += "Outbound Spam Policies Details:`nPolicy|AutoForwardingMode"
|
||||||
|
$details += $nonCompliantSpamPoliciesArray | ForEach-Object {
|
||||||
|
"$($_.Name)|$($_.AutoForwardingMode)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($failureReasons.Count -eq 0) {
|
||||||
|
$failureReasons = "N/A"
|
||||||
|
$details = "Both transport rules and outbound spam policies are configured correctly to block forwarding."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$failureReasons = $failureReasons -join " | "
|
||||||
|
$details = $details -join "`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $forwardingBlocked
|
||||||
|
Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,49 +1,82 @@
|
|||||||
function Test-BlockSharedMailboxSignIn {
|
function Test-BlockSharedMailboxSignIn {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.2.2"
|
||||||
|
|
||||||
|
# Conditions for 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: No shared mailboxes have the "Sign-in blocked" option disabled in the properties pane on the Microsoft 365 admin center.
|
||||||
|
# - Condition B: Using PowerShell, the `AccountEnabled` property for all shared mailboxes is set to `False`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: One or more shared mailboxes have the "Sign-in blocked" option enabled in the properties pane on the Microsoft 365 admin center.
|
||||||
|
# - Condition B: Using PowerShell, the `AccountEnabled` property for one or more shared mailboxes is set to `True`.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
|
try {
|
||||||
|
# Step: Retrieve shared mailbox details
|
||||||
|
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
||||||
|
|
||||||
# Retrieve shared mailbox details
|
# Step: Retrieve details of shared mailboxes from Azure AD (Condition B: Pass/Fail)
|
||||||
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
|
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
||||||
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
|
|
||||||
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
|
||||||
$allBlocked = $enabledMailboxes.Count -eq 0
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step: Identify enabled mailboxes (Condition B: Pass/Fail)
|
||||||
$failureReasons = if (-not $allBlocked) {
|
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
|
||||||
"Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')"
|
$allBlocked = $enabledMailboxes.Count -eq 0
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($allBlocked) {
|
# Step: Determine failure reasons based on enabled mailboxes (Condition A & B: Fail)
|
||||||
"All shared mailboxes have sign-in blocked."
|
$failureReasons = if (-not $allBlocked) {
|
||||||
}
|
"Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')"
|
||||||
else {
|
}
|
||||||
"Enabled Mailboxes: $($enabledMailboxes -join ', ')"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Step: Prepare details for the audit result (Condition A & B: Pass/Fail)
|
||||||
$params = @{
|
$details = if ($allBlocked) {
|
||||||
Rec = "1.2.2"
|
"All shared mailboxes have sign-in blocked."
|
||||||
Result = $allBlocked
|
}
|
||||||
Status = if ($allBlocked) { "Pass" } else { "Fail" }
|
else {
|
||||||
Details = $details
|
"Enabled Mailboxes: $($enabledMailboxes -join ', ')"
|
||||||
FailureReason = $failureReasons
|
}
|
||||||
|
|
||||||
|
# Step: Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allBlocked # Pass: Condition A, Condition B
|
||||||
|
Status = if ($allBlocked) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,47 +1,85 @@
|
|||||||
function Test-CommonAttachmentFilter {
|
function Test-CommonAttachmentFilter {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
<#
|
||||||
|
Conditions for 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
|
- Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`.
|
||||||
|
- Condition C: Ensure that the setting is enabled in the highest priority policy listed if custom policies exist.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: The Common Attachment Types Filter is not enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
|
- Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `False`.
|
||||||
|
- Condition C: Ensure that the setting is not enabled in the highest priority policy listed if custom policies exist.
|
||||||
|
#>
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
try {
|
||||||
|
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
|
||||||
|
# Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
|
# Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`.
|
||||||
|
|
||||||
# Retrieve the attachment filter policy
|
# Retrieve the attachment filter policy
|
||||||
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
|
||||||
$result = $attachmentFilter.EnableFileFilter
|
$result = $attachmentFilter.EnableFileFilter
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $result) {
|
$failureReasons = if (-not $result) {
|
||||||
"Common Attachment Types Filter is disabled"
|
# Condition A: The Common Attachment Types Filter is not enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
}
|
# Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `False`.
|
||||||
else {
|
"Common Attachment Types Filter is disabled"
|
||||||
"N/A"
|
}
|
||||||
}
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$details = if ($result) {
|
$details = if ($result) {
|
||||||
"File Filter Enabled: True"
|
"File Filter Enabled: True"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"File Filter Enabled: False"
|
"File Filter Enabled: False"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "2.1.2"
|
Rec = $recnum
|
||||||
Result = $result
|
Result = $result
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-CustomerLockbox {
|
function Test-CustomerLockbox {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
@@ -8,40 +9,71 @@ function Test-CustomerLockbox {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.3.6"
|
||||||
|
|
||||||
|
# Conditions for 1.3.6 (L2) Ensure the customer lockbox feature is enabled (Automated)
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: In the Microsoft 365 admin center, the box labeled "Customer Lockbox Requests" is checked.
|
||||||
|
# - Condition B: Using the SecureScore portal, the Customer Lockbox feature is enabled.
|
||||||
|
# - Condition C: Using PowerShell, the Customer Lockbox feature is set to `True`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: In the Microsoft 365 admin center, the box labeled "Customer Lockbox Requests" is not checked.
|
||||||
|
# - Condition B: Using the SecureScore portal, the Customer Lockbox feature is not enabled.
|
||||||
|
# - Condition C: Using PowerShell, the Customer Lockbox feature is not set to `True`.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled
|
try {
|
||||||
|
# Step: Retrieve the organization configuration (Condition C: Pass/Fail)
|
||||||
|
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
||||||
|
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
||||||
|
|
||||||
# Retrieve the organization configuration
|
# Step: Prepare failure reasons and details based on compliance (Condition A, B, & C: Fail)
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
|
$failureReasons = if (-not $customerLockboxEnabled) {
|
||||||
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
|
"Customer lockbox feature is not enabled."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step: Prepare details for the audit result (Condition A, B, & C: Pass/Fail)
|
||||||
$failureReasons = if (-not $customerLockboxEnabled) {
|
$details = if ($customerLockboxEnabled) {
|
||||||
"Customer lockbox feature is not enabled."
|
"Customer Lockbox Enabled: True"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"Customer Lockbox Enabled: False"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = if ($customerLockboxEnabled) {
|
# Step: Create and populate the CISAuditResult object
|
||||||
"Customer Lockbox Enabled: True"
|
$params = @{
|
||||||
}
|
Rec = $recnum
|
||||||
else {
|
Result = $customerLockboxEnabled
|
||||||
"Customer Lockbox Enabled: False"
|
Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object #
|
# Retrieve the description from the test definitions
|
||||||
$params = @{
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
Rec = "1.3.6"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
Result = $customerLockboxEnabled
|
|
||||||
Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-DialInBypassLobby {
|
function Test-DialInBypassLobby {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -9,39 +10,69 @@ function Test-DialInBypassLobby {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
|
||||||
|
|
||||||
# Retrieve Teams meeting policy for PSTN users
|
try {
|
||||||
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
|
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
|
||||||
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is set to `False`.
|
||||||
|
# - Condition B: Verification using the UI in the Microsoft Teams admin center confirms that "People dialing in can't bypass the lobby" is set to `Off`.
|
||||||
|
# - Condition C: Ensure that individuals who dial in by phone must wait in the lobby until admitted by a meeting organizer, co-organizer, or presenter.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`.
|
||||||
|
# - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`.
|
||||||
|
# - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby.
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve Teams meeting policy for PSTN users
|
||||||
$failureReasons = if (-not $PSTNBypassDisabled) {
|
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
|
||||||
"Users dialing in can bypass the lobby"
|
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($PSTNBypassDisabled) {
|
# Prepare failure reasons and details based on compliance
|
||||||
"AllowPSTNUsersToBypassLobby is set to False"
|
$failureReasons = if (-not $PSTNBypassDisabled) {
|
||||||
}
|
"Users dialing in can bypass the lobby"
|
||||||
else {
|
}
|
||||||
"AllowPSTNUsersToBypassLobby is set to True"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$details = if ($PSTNBypassDisabled) {
|
||||||
$params = @{
|
"AllowPSTNUsersToBypassLobby is set to False"
|
||||||
Rec = "8.5.4"
|
}
|
||||||
Result = $PSTNBypassDisabled
|
else {
|
||||||
Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
|
"AllowPSTNUsersToBypassLobby is set to True"
|
||||||
Details = $details
|
}
|
||||||
FailureReason = $failureReasons
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $PSTNBypassDisabled
|
||||||
|
Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-DisallowInfectedFilesDownload {
|
function Test-DisallowInfectedFilesDownload {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
@@ -10,40 +11,71 @@ function Test-DisallowInfectedFilesDownload {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.3.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
|
|
||||||
|
|
||||||
# Retrieve the SharePoint tenant configuration
|
try {
|
||||||
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
|
||||||
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DisallowInfectedFileDownload` setting is set to `True`.
|
||||||
|
# - Condition B: The setting prevents users from downloading infected files as detected by Defender for Office 365.
|
||||||
|
# - Condition C: Verification using the PowerShell command confirms that the setting is correctly configured.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DisallowInfectedFileDownload` setting is not set to `True`.
|
||||||
|
# - Condition B: The setting does not prevent users from downloading infected files.
|
||||||
|
# - Condition C: Verification using the PowerShell command indicates that the setting is incorrectly configured.
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve the SharePoint tenant configuration
|
||||||
$failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) {
|
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
|
||||||
"Downloading infected files is not disallowed."
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($isDisallowInfectedFileDownloadEnabled) {
|
# Condition A: The `DisallowInfectedFileDownload` setting is set to `True`
|
||||||
"DisallowInfectedFileDownload: True"
|
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
|
||||||
}
|
|
||||||
else {
|
|
||||||
"DisallowInfectedFileDownload: False"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Prepare failure reasons and details based on compliance
|
||||||
$params = @{
|
$failureReasons = if (-not $isDisallowInfectedFileDownloadEnabled) {
|
||||||
Rec = "7.3.1"
|
"Downloading infected files is not disallowed." # Condition B: The setting does not prevent users from downloading infected files
|
||||||
Result = $isDisallowInfectedFileDownloadEnabled
|
}
|
||||||
Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
|
else {
|
||||||
Details = $details
|
"N/A"
|
||||||
FailureReason = $failureReasons
|
}
|
||||||
}
|
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
|
|
||||||
|
$details = if ($isDisallowInfectedFileDownloadEnabled) {
|
||||||
|
"DisallowInfectedFileDownload: True" # Condition C: Verification confirms the setting is correctly configured
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"DisallowInfectedFileDownload: False" # Condition C: Verification indicates the setting is incorrectly configured
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isDisallowInfectedFileDownloadEnabled
|
||||||
|
Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-EnableDKIM {
|
function Test-EnableDKIM {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -8,41 +9,74 @@ function Test-EnableDKIM {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.9"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains (Automated)
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: DKIM is enabled for all Exchange Online domains in the Microsoft 365 security center.
|
||||||
|
- Condition B: Using the Exchange Online PowerShell Module, the `CnameConfiguration.Enabled` property for each domain is set to `True`.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: DKIM is not enabled for one or more Exchange Online domains in the Microsoft 365 security center.
|
||||||
|
- Condition B: Using the Exchange Online PowerShell Module, the `CnameConfiguration.Enabled` property for one or more domains is set to `False`.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
|
|
||||||
|
|
||||||
# Retrieve DKIM configuration for all domains
|
try {
|
||||||
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
|
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
|
||||||
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
|
|
||||||
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve DKIM configuration for all domains
|
||||||
$failureReasons = if (-not $dkimResult) {
|
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
|
||||||
"DKIM is not enabled for some domains"
|
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
|
||||||
}
|
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($dkimResult) {
|
# Prepare failure reasons and details based on compliance
|
||||||
"All domains have DKIM enabled"
|
$failureReasons = if (-not $dkimResult) {
|
||||||
}
|
"DKIM is not enabled for some domains" # Condition A fail
|
||||||
else {
|
}
|
||||||
"DKIM not enabled for: $($dkimFailedDomains -join ', ')"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$details = if ($dkimResult) {
|
||||||
$params = @{
|
"All domains have DKIM enabled" # Condition A pass
|
||||||
Rec = "2.1.9"
|
}
|
||||||
Result = $dkimResult
|
else {
|
||||||
Status = if ($dkimResult) { "Pass" } else { "Fail" }
|
"DKIM not enabled for: $($dkimFailedDomains -join ', ')" # Condition B fail
|
||||||
Details = $details
|
}
|
||||||
FailureReason = $failureReasons
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $dkimResult
|
||||||
|
Status = if ($dkimResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-ExternalNoControl {
|
function Test-ExternalNoControl {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -7,42 +8,72 @@ function Test-ExternalNoControl {
|
|||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
# . .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.7 (L1) Ensure external participants can't give or request control
|
|
||||||
|
|
||||||
# Retrieve Teams meeting policy for external participant control
|
try {
|
||||||
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
|
# 8.5.7 (L1) Ensure external participants can't give or request control
|
||||||
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Ensure the `AllowExternalParticipantGiveRequestControl` setting in Teams is set to `False`.
|
||||||
|
# - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command.
|
||||||
|
# - Condition C: Verification using the UI confirms that external participants are unable to give or request control.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`.
|
||||||
|
# - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command.
|
||||||
|
# - Condition C: Verification using the UI indicates that external participants can give or request control.
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve Teams meeting policy for external participant control
|
||||||
$failureReasons = if (-not $externalControlRestricted) {
|
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
|
||||||
"External participants can give or request control"
|
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($externalControlRestricted) {
|
# Prepare failure reasons and details based on compliance
|
||||||
"AllowExternalParticipantGiveRequestControl is set to False"
|
$failureReasons = if (-not $externalControlRestricted) {
|
||||||
}
|
"External participants can give or request control"
|
||||||
else {
|
}
|
||||||
"AllowExternalParticipantGiveRequestControl is set to True"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$details = if ($externalControlRestricted) {
|
||||||
$params = @{
|
"AllowExternalParticipantGiveRequestControl is set to False"
|
||||||
Rec = "8.5.7"
|
}
|
||||||
Result = $externalControlRestricted
|
else {
|
||||||
Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
|
"AllowExternalParticipantGiveRequestControl is set to True"
|
||||||
Details = $details
|
}
|
||||||
FailureReason = $failureReasons
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $externalControlRestricted
|
||||||
|
Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-ExternalSharingCalendars {
|
function Test-ExternalSharingCalendars {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -10,48 +11,76 @@ function Test-ExternalSharingCalendars {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.3.3"
|
||||||
|
|
||||||
|
# Conditions for 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: In the Microsoft 365 admin center, external calendar sharing is disabled.
|
||||||
|
# - Condition B: Using the Exchange Online PowerShell Module, the `OrganizationConfig` property `ExternalSharingEnabled` is set to `False`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: In the Microsoft 365 admin center, external calendar sharing is enabled.
|
||||||
|
# - Condition B: Using the Exchange Online PowerShell Module, the `OrganizationConfig` property `ExternalSharingEnabled` is set to `True`.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
|
try {
|
||||||
|
# Step: Retrieve sharing policies related to calendar sharing
|
||||||
|
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
||||||
|
|
||||||
# Retrieve sharing policies related to calendar sharing
|
# Step (Condition A & B: Pass/Fail): Check if calendar sharing is disabled in all applicable policies
|
||||||
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
|
$isExternalSharingDisabled = $true
|
||||||
|
$sharingPolicyDetails = @()
|
||||||
# Check if calendar sharing is disabled in all applicable policies
|
foreach ($policy in $sharingPolicies) {
|
||||||
$isExternalSharingDisabled = $true
|
if ($policy.Enabled -eq $true) {
|
||||||
$sharingPolicyDetails = @()
|
$isExternalSharingDisabled = $false
|
||||||
foreach ($policy in $sharingPolicies) {
|
$sharingPolicyDetails += "$($policy.Name): Enabled"
|
||||||
if ($policy.Enabled -eq $true) {
|
}
|
||||||
$isExternalSharingDisabled = $false
|
|
||||||
$sharingPolicyDetails += "$($policy.Name): Enabled"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step: Prepare failure reasons and details based on compliance (Condition A & B: Fail)
|
||||||
$failureReasons = if (-not $isExternalSharingDisabled) {
|
$failureReasons = if (-not $isExternalSharingDisabled) {
|
||||||
"Calendar sharing with external users is enabled in one or more policies."
|
"Calendar sharing with external users is enabled in one or more policies."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = if ($isExternalSharingDisabled) {
|
# Step: Prepare details for the audit result (Condition A & B: Pass/Fail)
|
||||||
"Calendar sharing with external users is disabled."
|
$details = if ($isExternalSharingDisabled) {
|
||||||
}
|
"Calendar sharing with external users is disabled."
|
||||||
else {
|
}
|
||||||
"Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')"
|
else {
|
||||||
}
|
"Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Step: Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "1.3.3"
|
Rec = $recnum
|
||||||
Result = $isExternalSharingDisabled
|
Result = $isExternalSharingDisabled
|
||||||
Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
|
Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,48 +1,85 @@
|
|||||||
function Test-GlobalAdminsCount {
|
function Test-GlobalAdminsCount {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
# Conditions for 1.1.3 (L1) Ensure that between two and four global admins are designated
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The number of global admins is at least 2.
|
||||||
|
# - Condition B: The number of global admins is at most 4.
|
||||||
|
# - Condition C: The list of global admin usernames is accurately retrieved and displayed.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The number of global admins is less than 2.
|
||||||
|
# - Condition B: The number of global admins is more than 4.
|
||||||
|
# - Condition C: Any discrepancies or errors in retrieving the list of global admin usernames.
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.1.3 (L1) Ensure that between two and four global admins are designated
|
try {
|
||||||
|
# Step: Retrieve global admin role
|
||||||
|
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
|
||||||
|
|
||||||
# Retrieve global admin role and members
|
# Step: Retrieve global admin members
|
||||||
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
|
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
||||||
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
|
|
||||||
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
|
|
||||||
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step: Count the number of global admins
|
||||||
$failureReasons = if ($globalAdminCount -lt 2) {
|
$globalAdminCount = $globalAdmins.Count
|
||||||
"Less than 2 global admins: $globalAdminUsernames"
|
|
||||||
}
|
|
||||||
elseif ($globalAdminCount -gt 4) {
|
|
||||||
"More than 4 global admins: $globalAdminUsernames"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
|
# Step: Retrieve and format the usernames of global admins
|
||||||
|
$globalAdminUsernames = ($globalAdmins | ForEach-Object {
|
||||||
|
"$($_.AdditionalProperties["displayName"]) ($($_.AdditionalProperties["userPrincipalName"]))"
|
||||||
|
}) -join ', '
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Step: Determine failure reasons based on global admin count
|
||||||
$params = @{
|
$failureReasons = if ($globalAdminCount -lt 2) {
|
||||||
Rec = "1.1.3"
|
"Less than 2 global admins: $globalAdminUsernames"
|
||||||
Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
|
}
|
||||||
Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
|
elseif ($globalAdminCount -gt 4) {
|
||||||
Details = $details
|
"More than 4 global admins: $globalAdminUsernames"
|
||||||
FailureReason = $failureReasons
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Step: Prepare details for the audit result
|
||||||
|
$details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
|
||||||
|
|
||||||
|
# Step: Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
|
||||||
|
Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-GuestAccessExpiration {
|
function Test-GuestAccessExpiration {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
@@ -10,34 +11,64 @@ function Test-GuestAccessExpiration {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.9"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
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
|
try {
|
||||||
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
|
# 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
|
||||||
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The ExternalUserExpirationRequired setting in SharePoint is set to True.
|
||||||
|
# - Condition B: The ExternalUserExpireInDays setting in SharePoint is configured to 30 or less.
|
||||||
|
# - Condition C: Verification using the SharePoint Admin Center confirms that guest access will expire automatically after the specified number of days.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The ExternalUserExpirationRequired setting in SharePoint is not set to True.
|
||||||
|
# - Condition B: The ExternalUserExpireInDays setting in SharePoint is configured to more than 30 days.
|
||||||
|
# - Condition C: Verification using the SharePoint Admin Center indicates that guest access is not set to expire automatically after the specified number of days.
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve SharePoint tenant settings related to guest access expiration
|
||||||
$failureReasons = if (-not $isGuestAccessExpirationConfiguredCorrectly) {
|
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
|
||||||
"Guest access expiration is not configured to automatically expire within 30 days or less."
|
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
|
||||||
}
|
|
||||||
else {
|
# Prepare failure reasons and details based on compliance
|
||||||
"N/A"
|
$failureReasons = if (-not $isGuestAccessExpirationConfiguredCorrectly) {
|
||||||
|
"Guest access expiration is not configured to automatically expire within 30 days or less."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isGuestAccessExpirationConfiguredCorrectly
|
||||||
|
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "7.2.9"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $isGuestAccessExpirationConfiguredCorrectly
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-GuestUsersBiweeklyReview {
|
function Test-GuestUsersBiweeklyReview {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
@@ -10,41 +11,56 @@ function Test-GuestUsersBiweeklyReview {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.1.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.1.4 (L1) Ensure Guest Users are reviewed at least biweekly
|
try {
|
||||||
|
# 1.1.4 (L1) Ensure Guest Users are reviewed at least biweekly
|
||||||
|
|
||||||
|
|
||||||
# Retrieve guest users from Microsoft Graph
|
# Retrieve guest users from Microsoft Graph
|
||||||
# Connect-MgGraph -Scopes "User.Read.All"
|
# Connect-MgGraph -Scopes "User.Read.All"
|
||||||
$guestUsers = Get-MgUser -All -Filter "UserType eq 'Guest'"
|
$guestUsers = Get-MgUser -All -Filter "UserType eq 'Guest'"
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($guestUsers) {
|
$failureReasons = if ($guestUsers) {
|
||||||
"Guest users present: $($guestUsers.Count)"
|
"Guest users present: $($guestUsers.Count)"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = if ($guestUsers) {
|
$details = if ($guestUsers) {
|
||||||
$auditCommand = "Get-MgUser -All -Property UserType,UserPrincipalName | Where {`$_.UserType -ne 'Member'} | Format-Table UserPrincipalName, UserType"
|
$auditCommand = "Get-MgUser -All -Property UserType,UserPrincipalName | Where {`$_.UserType -ne 'Member'} | Format-Table UserPrincipalName, UserType"
|
||||||
"Manual review required. To list guest users, run: `"$auditCommand`"."
|
"Manual review required. To list guest users, run: `"$auditCommand`"."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"No guest users found."
|
"No guest users found."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "1.1.4"
|
Rec = $recnum
|
||||||
Result = -not $guestUsers
|
Result = -not $guestUsers
|
||||||
Status = if ($guestUsers) { "Fail" } else { "Pass" }
|
Status = if ($guestUsers) { "Fail" } else { "Pass" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-IdentifyExternalEmail {
|
function Test-IdentifyExternalEmail {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -10,34 +11,68 @@ function Test-IdentifyExternalEmail {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.2.3"
|
||||||
|
|
||||||
|
# Conditions for 6.2.3 (L1) Ensure email from external senders is identified
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: External tagging is enabled using PowerShell for all identities.
|
||||||
|
# - Condition B: The BypassAllowList only contains email addresses the organization has permitted to bypass external tagging.
|
||||||
|
# - Condition C: External sender tag appears in email messages received from external sources.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: External tagging is not enabled using PowerShell for all identities.
|
||||||
|
# - Condition B: The BypassAllowList contains unauthorized email addresses.
|
||||||
|
# - Condition C: External sender tag does not appear in email messages received from external sources.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.3 (L1) Ensure email from external senders is identified
|
|
||||||
|
|
||||||
# Retrieve external sender tagging configuration
|
try {
|
||||||
$externalInOutlook = Get-ExternalInOutlook
|
# 6.2.3 (L1) Ensure email from external senders is identified
|
||||||
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve external sender tagging configuration
|
||||||
$failureReasons = if (-not $externalTaggingEnabled) {
|
$externalInOutlook = Get-ExternalInOutlook
|
||||||
"External sender tagging is disabled"
|
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
|
||||||
}
|
|
||||||
else {
|
# Prepare failure reasons and details based on compliance
|
||||||
"N/A"
|
$failureReasons = if (-not $externalTaggingEnabled) {
|
||||||
|
# Condition A: External tagging is not enabled using PowerShell for all identities.
|
||||||
|
"External sender tagging is disabled"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Details for external tagging configuration
|
||||||
|
$details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $externalTaggingEnabled
|
||||||
|
Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "6.2.3"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $externalTaggingEnabled
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-LinkSharingRestrictions {
|
function Test-LinkSharingRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -10,34 +11,64 @@ function Test-LinkSharingRestrictions {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.7"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
|
try {
|
||||||
|
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DefaultSharingLinkType` setting in SharePoint and OneDrive is set to `Direct`.
|
||||||
|
# - Condition B: The setting `Choose the type of link that's selected by default when users share files and folders in SharePoint and OneDrive` is set to `Specific people (only the people the user specifies)`.
|
||||||
|
# - Condition C: Verification using the UI confirms that the link sharing settings are configured as recommended.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DefaultSharingLinkType` setting in SharePoint and OneDrive is not set to `Direct`.
|
||||||
|
# - Condition B: The setting `Choose the type of link that's selected by default when users share files and folders in SharePoint and OneDrive` is not set to `Specific people (only the people the user specifies)`.
|
||||||
|
# - Condition C: Verification using the UI indicates that the link sharing settings are not configured as recommended.
|
||||||
|
|
||||||
# Retrieve link sharing configuration for SharePoint and OneDrive
|
# Retrieve link sharing configuration for SharePoint and OneDrive
|
||||||
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
|
||||||
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isLinkSharingRestricted
|
||||||
|
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
# 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"
|
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "7.2.7"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $isLinkSharingRestricted
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-MailTipsEnabled {
|
function Test-MailTipsEnabled {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -11,40 +12,75 @@ function Test-MailTipsEnabled {
|
|||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "6.5.2"
|
||||||
|
|
||||||
|
# Conditions for 6.5.2 (L2) Ensure MailTips are enabled for end users
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: MailTipsAllTipsEnabled is set to True.
|
||||||
|
# - Condition B: MailTipsExternalRecipientsTipsEnabled is set to True.
|
||||||
|
# - Condition C: MailTipsGroupMetricsEnabled is set to True.
|
||||||
|
# - Condition D: MailTipsLargeAudienceThreshold is set to an acceptable value (default is 25).
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: MailTipsAllTipsEnabled is not set to True.
|
||||||
|
# - Condition B: MailTipsExternalRecipientsTipsEnabled is not set to True.
|
||||||
|
# - Condition C: MailTipsGroupMetricsEnabled is not set to True.
|
||||||
|
# - Condition D: MailTipsLargeAudienceThreshold is not set to an acceptable value (default is 25).
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
try {
|
||||||
|
# 6.5.2 (L2) Ensure MailTips are enabled for end users
|
||||||
|
|
||||||
# Retrieve organization configuration for MailTips settings
|
# Retrieve organization configuration for MailTips settings
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
|
||||||
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
|
||||||
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Check the MailTips settings (Conditions A, B, C, D)
|
||||||
$failureReasons = if (-not ($allTipsEnabled -and $externalRecipientsTipsEnabled)) {
|
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
|
||||||
"One or more MailTips settings are not configured as required."
|
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) {
|
# Prepare failure reasons and details based on compliance
|
||||||
"MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
$failureReasons = if (-not ($allTipsEnabled -and $externalRecipientsTipsEnabled)) {
|
||||||
}
|
"One or more MailTips settings are not configured as required."
|
||||||
else {
|
}
|
||||||
"One or more MailTips settings are not configured as required."
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$details = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) {
|
||||||
$params = @{
|
"MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
|
||||||
Rec = "6.5.2"
|
}
|
||||||
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
else {
|
||||||
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
|
"One or more MailTips settings are not configured as required."
|
||||||
Details = $details
|
}
|
||||||
FailureReason = $failureReasons
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
|
||||||
|
Status = if ($allTipsEnabled -and $externalRecipientsTipsEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,114 +1,147 @@
|
|||||||
function Test-MailboxAuditingE3 {
|
function Test-MailboxAuditingE3 {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
|
||||||
# Create Table for Details
|
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
<#
|
||||||
|
Conditions for 6.1.2 (L1) Ensure mailbox auditing for E3 users is Enabled
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Mailbox audit logging is enabled for all user mailboxes.
|
||||||
|
- Condition B: The `AuditAdmin` actions include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
|
||||||
|
- Condition C: The `AuditDelegate` actions include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
|
||||||
|
- Condition D: The `AuditOwner` actions include `ApplyRecord`, `HardDelete`, `MoveToDeletedItems`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Mailbox audit logging is not enabled for all user mailboxes.
|
||||||
|
- Condition B: The `AuditAdmin` actions do not include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
|
||||||
|
- Condition C: The `AuditDelegate` actions do not include `ApplyRecord`, `Create`, `HardDelete`, `MoveToDeletedItems`, `SendAs`, `SendOnBehalf`, `SoftDelete`, `Update`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
|
||||||
|
- Condition D: The `AuditOwner` actions do not include `ApplyRecord`, `HardDelete`, `MoveToDeletedItems`, `SoftDelete`, `Update`, `UpdateCalendarDelegation`, `UpdateFolderPermissions`, and `UpdateInboxRules`.
|
||||||
|
#>
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION")
|
$e3SkuPartNumber = "SPE_E3"
|
||||||
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
|
|
||||||
|
|
||||||
$allFailures = @()
|
$allFailures = @()
|
||||||
$allUsers = Get-AzureADUser -All $true
|
#$allUsers = Get-AzureADUser -All $true
|
||||||
|
$founde3Sku = Get-MgSubscribedSku -All | Where-Object {$_.SkuPartNumber -eq $e3SkuPartNumber}
|
||||||
$processedUsers = @{} # Dictionary to track processed users
|
$processedUsers = @{} # Dictionary to track processed users
|
||||||
|
$recnum = "6.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
|
||||||
foreach ($user in $allUsers) {
|
|
||||||
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
|
||||||
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
|
||||||
$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
|
|
||||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
|
|
||||||
|
|
||||||
if ($hasOfficeE3) {
|
process {
|
||||||
|
if (($founde3Sku.count)-ne 0) {
|
||||||
|
$allUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde3Sku.SkuId) )" -All
|
||||||
|
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||||
|
try {
|
||||||
|
foreach ($user in $allUsers) {
|
||||||
|
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
||||||
|
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
#$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
||||||
|
#$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
|
||||||
|
#Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
|
||||||
|
|
||||||
$userUPN = $user.UserPrincipalName
|
$userUPN = $user.UserPrincipalName
|
||||||
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
$mailbox = $mailboxes | Where-Object { $_.UserPrincipalName -eq $user.UserPrincipalName }
|
||||||
|
|
||||||
$missingActions = @()
|
$missingActions = @()
|
||||||
if ($mailbox.AuditEnabled) {
|
if ($mailbox.AuditEnabled) {
|
||||||
foreach ($action in $AdminActions) {
|
foreach ($action in $AdminActions) {
|
||||||
|
# Condition B: Checking if the `AuditAdmin` actions include required actions
|
||||||
if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" }
|
if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" }
|
||||||
}
|
}
|
||||||
foreach ($action in $DelegateActions) {
|
foreach ($action in $DelegateActions) {
|
||||||
|
# Condition C: Checking if the `AuditDelegate` actions include required actions
|
||||||
if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" }
|
if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" }
|
||||||
}
|
}
|
||||||
foreach ($action in $OwnerActions) {
|
foreach ($action in $OwnerActions) {
|
||||||
|
# Condition D: Checking if the `AuditOwner` actions include required actions
|
||||||
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($missingActions.Count -gt 0) {
|
||||||
|
$formattedActions = Format-MissingAction -missingActions $missingActions
|
||||||
|
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$allFailures += "$userUPN`: AuditEnabled - False"
|
# Condition A: Checking if mailbox audit logging is enabled
|
||||||
continue
|
$allFailures += "$userUPN|False|||"
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
|
||||||
}
|
|
||||||
# Mark the user as processed
|
# Mark the user as processed
|
||||||
$processedUsers[$user.UserPrincipalName] = $true
|
$processedUsers[$user.UserPrincipalName] = $true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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 {
|
||||||
|
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
# Prepare failure reasons and details based on compliance
|
$params = @{
|
||||||
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
Rec = $recnum
|
||||||
$details = if ($allFailures.Count -eq 0) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
Result = $false
|
||||||
|
Status = "Fail"
|
||||||
# Populate the audit result
|
Details = "No M365 E3 licenses found."
|
||||||
$params = @{
|
FailureReason = "The audit is for M365 E3 licenses, but no such licenses were found."
|
||||||
Rec = "6.1.2"
|
}
|
||||||
Result = $allFailures.Count -eq 0
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
#$verbosePreference = 'Continue'
|
||||||
|
$detailsLength = $details.Length
|
||||||
|
Write-Verbose "Character count of the details: $detailsLength"
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
|
||||||
param ([array]$missingActions)
|
|
||||||
|
|
||||||
$actionGroups = @{
|
|
||||||
"Admin" = @()
|
|
||||||
"Delegate" = @()
|
|
||||||
"Owner" = @()
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($action in $missingActions) {
|
|
||||||
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
|
|
||||||
$type = $matches[1]
|
|
||||||
$actionName = $matches[2]
|
|
||||||
$actionGroups[$type] += $actionName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$formattedResults = @()
|
|
||||||
foreach ($type in $actionGroups.Keys) {
|
|
||||||
if ($actionGroups[$type].Count -gt 0) {
|
|
||||||
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formattedResults -join '; '
|
|
||||||
}
|
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
function Test-MailboxAuditingE5 {
|
function Test-MailboxAuditingE5 {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
|
||||||
# Create Table for Details
|
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -10,111 +9,135 @@ function Test-MailboxAuditingE5 {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
|
# Conditions for 6.1.3 (L1) Ensure mailbox auditing for E5 users is Enabled
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Mailbox auditing is enabled for E5 users.
|
||||||
|
# - Condition B: AuditAdmin actions include ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SendAs, SendOnBehalf, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
# - Condition C: AuditDelegate actions include ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
# - Condition D: AuditOwner actions include ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Mailbox auditing is not enabled for E5 users.
|
||||||
|
# - Condition B: AuditAdmin actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SendAs, SendOnBehalf, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
# - Condition C: AuditDelegate actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
# - Condition D: AuditOwner actions do not include all of the following: ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
|
||||||
|
$e5SkuPartNumber = "SPE_E5"
|
||||||
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$allFailures = @()
|
$allFailures = @()
|
||||||
$allUsers = Get-AzureADUser -All $true
|
#$allUsers = Get-AzureADUser -All $true
|
||||||
|
$founde5Sku = Get-MgSubscribedSku -All | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber }
|
||||||
$processedUsers = @{} # Dictionary to track processed users
|
$processedUsers = @{} # Dictionary to track processed users
|
||||||
|
$recnum = "6.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
foreach ($user in $allUsers) {
|
if (($founde5Sku.count) -ne 0) {
|
||||||
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
$allUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde5Sku.SkuId) )" -All
|
||||||
continue
|
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
foreach ($user in $allUsers) {
|
||||||
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
||||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
||||||
if ($hasOfficeE5) {
|
|
||||||
$userUPN = $user.UserPrincipalName
|
|
||||||
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
|
||||||
|
|
||||||
$missingActions = @()
|
|
||||||
if ($mailbox.AuditEnabled) {
|
|
||||||
foreach ($action in $AdminActions) {
|
|
||||||
if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" }
|
|
||||||
}
|
|
||||||
foreach ($action in $DelegateActions) {
|
|
||||||
if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" }
|
|
||||||
}
|
|
||||||
foreach ($action in $OwnerActions) {
|
|
||||||
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$allFailures += "$userUPN`: AuditEnabled - False"
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($missingActions) {
|
#$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
||||||
$formattedActions = Format-MissingActions $missingActions
|
#$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
||||||
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
|
#Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
||||||
|
$mailbox = $mailboxes | Where-Object { $_.UserPrincipalName -eq $user.UserPrincipalName }
|
||||||
|
$userUPN = $user.UserPrincipalName
|
||||||
|
#$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
||||||
|
|
||||||
|
$missingActions = @()
|
||||||
|
if ($mailbox.AuditEnabled) {
|
||||||
|
# Validate Admin actions
|
||||||
|
foreach ($action in $AdminActions) {
|
||||||
|
if ($mailbox.AuditAdmin -notcontains $action) { $missingActions += "Admin action '$action' missing" } # Condition B
|
||||||
|
}
|
||||||
|
# Validate Delegate actions
|
||||||
|
foreach ($action in $DelegateActions) {
|
||||||
|
if ($mailbox.AuditDelegate -notcontains $action) { $missingActions += "Delegate action '$action' missing" } # Condition C
|
||||||
|
}
|
||||||
|
# Validate Owner actions
|
||||||
|
foreach ($action in $OwnerActions) {
|
||||||
|
if ($mailbox.AuditOwner -notcontains $action) { $missingActions += "Owner action '$action' missing" } # Condition D
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($missingActions.Count -gt 0) {
|
||||||
|
$formattedActions = Format-MissingAction -missingActions $missingActions
|
||||||
|
$allFailures += "$userUPN|True|$($formattedActions.Admin)|$($formattedActions.Delegate)|$($formattedActions.Owner)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Write-Verbose "User $($user.UserPrincipalName) passed the mailbox audit checks."
|
$allFailures += "$userUPN|False|||"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Mark the user as processed
|
||||||
$processedUsers[$user.UserPrincipalName] = $true
|
$processedUsers[$user.UserPrincipalName] = $true
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
# Adding verbose output to indicate the user does not have an E5 license
|
# Prepare failure reasons and details based on compliance
|
||||||
Write-Verbose "User $($user.UserPrincipalName) does not have an Office E5 license."
|
$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." # Condition A for pass
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n") # Condition A for fail
|
||||||
|
}
|
||||||
|
|
||||||
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
# Prepare failure reasons and details based on compliance
|
$params = @{
|
||||||
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
Rec = $recnum
|
||||||
$details = if ($allFailures.Count -eq 0) { "All Office E5 users have correct mailbox audit settings." } else { $allFailures -join " | " }
|
Result = $false
|
||||||
|
Status = "Fail"
|
||||||
# Populate the audit result
|
Details = "No M365 E5 licenses found."
|
||||||
$params = @{
|
FailureReason = "The audit is for M365 E5 licenses, but no such licenses were found."
|
||||||
Rec = "6.1.3"
|
}
|
||||||
Result = $allFailures.Count -eq 0
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
#$verbosePreference = 'Continue'
|
||||||
|
$detailsLength = $details.Length
|
||||||
|
Write-Verbose "Character count of the details: $detailsLength"
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Format-MissingActions {
|
|
||||||
param ([array]$missingActions)
|
|
||||||
|
|
||||||
$actionGroups = @{
|
|
||||||
"Admin" = @()
|
|
||||||
"Delegate" = @()
|
|
||||||
"Owner" = @()
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($action in $missingActions) {
|
|
||||||
if ($action -match "(Admin|Delegate|Owner) action '([^']+)' missing") {
|
|
||||||
$type = $matches[1]
|
|
||||||
$actionName = $matches[2]
|
|
||||||
$actionGroups[$type] += $actionName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$formattedResults = @()
|
|
||||||
foreach ($type in $actionGroups.Keys) {
|
|
||||||
if ($actionGroups[$type].Count -gt 0) {
|
|
||||||
$formattedResults += "$($type) actions missing: $($actionGroups[$type] -join ', ')"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formattedResults -join '; '
|
|
||||||
}
|
|
||||||
|
@@ -1,52 +1,80 @@
|
|||||||
function Test-ManagedApprovedPublicGroups {
|
function Test-ManagedApprovedPublicGroups {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
|
||||||
|
|
||||||
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.2.1"
|
||||||
|
|
||||||
|
# Conditions for 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: No groups have the status 'Public' in the privacy column on the Active teams and groups page.
|
||||||
|
# - Condition B: Using Microsoft Graph PowerShell, all groups return a status other than 'Public' when checked.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: One or more groups have the status 'Public' in the privacy column on the Active teams and groups page.
|
||||||
|
# - Condition B: Using Microsoft Graph PowerShell, one or more groups return a status of 'Public' when checked.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
|
try {
|
||||||
|
# Step: Retrieve all groups with visibility set to 'Public'
|
||||||
|
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
||||||
|
|
||||||
# Retrieve all public groups
|
# Step: Determine failure reasons based on the presence of public groups
|
||||||
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
|
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {
|
||||||
|
"There are public groups present that are not organizationally managed/approved."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step: Prepare details for the audit result
|
||||||
$failureReasons = if ($null -ne $allGroups -and $allGroups.Count -gt 0) {
|
$details = if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
|
||||||
"There are public groups present that are not organizationally managed/approved."
|
"No public groups found."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
||||||
}
|
"Public groups found: $($groupDetails -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
$details = if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
|
# Step: Create and populate the CISAuditResult object
|
||||||
"No public groups found."
|
$params = @{
|
||||||
}
|
Rec = $recnum
|
||||||
else {
|
Result = $null -eq $allGroups -or $allGroups.Count -eq 0
|
||||||
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
|
Status = if ($null -eq $allGroups -or $allGroups.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
"Public groups found: $($groupDetails -join ', ')"
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Retrieve the description from the test definitions
|
||||||
$params = @{
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
Rec = "1.2.1"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
Result = $null -eq $allGroups -or $allGroups.Count -eq 0
|
|
||||||
Status = if ($null -eq $allGroups -or $allGroups.Count -eq 0) { "Pass" } else { "Fail" }
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResults
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-MeetingChatNoAnonymous {
|
function Test-MeetingChatNoAnonymous {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -9,36 +10,64 @@ function Test-MeetingChatNoAnonymous {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
try {
|
||||||
|
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `MeetingChatEnabledType` setting in Teams is set to `EnabledExceptAnonymous`.
|
||||||
|
# - Condition B: The setting for meeting chat is configured to allow chat for everyone except anonymous users.
|
||||||
|
# - Condition C: Verification using the Teams Admin Center confirms that the meeting chat settings are configured as recommended.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`.
|
||||||
|
# - Condition B: The setting for meeting chat allows chat for anonymous users.
|
||||||
|
# - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended.
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Retrieve the Teams meeting policy for meeting chat
|
||||||
|
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
||||||
|
# Condition A: Check if the MeetingChatEnabledType is set to 'EnabledExceptAnonymous'
|
||||||
|
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
|
||||||
|
|
||||||
# Retrieve the Teams meeting policy for meeting chat
|
# Prepare failure reasons and details based on compliance
|
||||||
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
|
$failureReasons = if ($chatAnonDisabled) {
|
||||||
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
|
"N/A"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Meeting chat allows anonymous users"
|
||||||
|
}
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
$details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
|
||||||
$failureReasons = if ($chatAnonDisabled) {
|
|
||||||
"N/A"
|
# Create and populate the CISAuditResult object
|
||||||
}
|
$params = @{
|
||||||
else {
|
Rec = $recnum
|
||||||
"Meeting chat allows anonymous users"
|
Result = $chatAnonDisabled
|
||||||
|
Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "8.5.5"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $chatAnonDisabled
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-ModernAuthExchangeOnline {
|
function Test-ModernAuthExchangeOnline {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -8,7 +9,24 @@ function Test-ModernAuthExchangeOnline {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
|
||||||
|
# Conditions for 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Modern authentication for Exchange Online is enabled.
|
||||||
|
# - Condition B: Exchange Online clients use modern authentication to log in to Microsoft 365 mailboxes.
|
||||||
|
# - Condition C: Users of older email clients, such as Outlook 2013 and Outlook 2016, are no longer able to authenticate to Exchange using Basic Authentication.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Modern authentication for Exchange Online is not enabled.
|
||||||
|
# - Condition B: Exchange Online clients do not use modern authentication to log in to Microsoft 365 mailboxes.
|
||||||
|
# - Condition C: Users of older email clients, such as Outlook 2013 and Outlook 2016, are still able to authenticate to Exchange using Basic Authentication.
|
||||||
|
|
||||||
|
$recnum = "6.5.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
@@ -16,6 +34,8 @@ function Test-ModernAuthExchangeOnline {
|
|||||||
# Ensuring the ExchangeOnlineManagement module is available
|
# Ensuring the ExchangeOnlineManagement module is available
|
||||||
|
|
||||||
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
|
||||||
|
|
||||||
|
# Check modern authentication setting in Exchange Online configuration (Condition A and B)
|
||||||
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
|
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
@@ -26,22 +46,33 @@ function Test-ModernAuthExchangeOnline {
|
|||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Details include the current setting (Condition A and B)
|
||||||
$details = "OAuth2ClientProfileEnabled: $($orgConfig.OAuth2ClientProfileEnabled) for Organization: $($orgConfig.Name)"
|
$details = "OAuth2ClientProfileEnabled: $($orgConfig.OAuth2ClientProfileEnabled) for Organization: $($orgConfig.Name)"
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "6.5.1"
|
Rec = $recnum
|
||||||
Result = $orgConfig.OAuth2ClientProfileEnabled
|
Result = $orgConfig.OAuth2ClientProfileEnabled
|
||||||
Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
|
Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
Write-Error "An error occurred while testing modern authentication for Exchange Online: $_"
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-ModernAuthSharePoint {
|
function Test-ModernAuthSharePoint {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -8,33 +9,65 @@ function Test-ModernAuthSharePoint {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.1"
|
||||||
|
|
||||||
|
<#
|
||||||
|
# Conditions for 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
|
||||||
|
|
||||||
|
## Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The setting "Apps that don't use modern authentication" is set to "Block access" in the SharePoint admin center.
|
||||||
|
# - Condition B: The PowerShell command `Get-SPOTenant | ft LegacyAuthProtocolsEnabled` returns `False`.
|
||||||
|
|
||||||
|
## Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The setting "Apps that don't use modern authentication" is not set to "Block access" in the SharePoint admin center.
|
||||||
|
# - Condition B: The PowerShell command `Get-SPOTenant | ft LegacyAuthProtocolsEnabled` returns `True`.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
|
||||||
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
|
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
|
||||||
|
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $modernAuthForSPRequired) {
|
$failureReasons = if (-not $modernAuthForSPRequired) {
|
||||||
"Legacy authentication protocols are enabled"
|
"Legacy authentication protocols are enabled" # Fail Condition B
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)"
|
$details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)" # Details for Condition B
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "7.2.1"
|
Rec = $recnum
|
||||||
Result = $modernAuthForSPRequired
|
Result = $modernAuthForSPRequired
|
||||||
Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
|
Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-NoAnonymousMeetingJoin {
|
function Test-NoAnonymousMeetingJoin {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -9,35 +10,64 @@ function Test-NoAnonymousMeetingJoin {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
|
try {
|
||||||
|
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: `AllowAnonymousUsersToJoinMeeting` is set to `False`.
|
||||||
|
# - Condition B: Verification using the UI confirms that `Anonymous users can join a meeting` is set to `Off` in the Global meeting policy.
|
||||||
|
# - Condition C: PowerShell command output indicates that anonymous users are not allowed to join meetings.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`.
|
||||||
|
# - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy.
|
||||||
|
# - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings.
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
|
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
|
||||||
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
|
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($allowAnonymousUsersToJoinMeeting) {
|
$failureReasons = if ($allowAnonymousUsersToJoinMeeting) {
|
||||||
"Anonymous users are allowed to join meetings"
|
"Anonymous users are allowed to join meetings"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = -not $allowAnonymousUsersToJoinMeeting
|
||||||
|
Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "8.5.1"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = -not $allowAnonymousUsersToJoinMeeting
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-NoAnonymousMeetingStart {
|
function Test-NoAnonymousMeetingStart {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -9,35 +10,65 @@ function Test-NoAnonymousMeetingStart {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
|
try {
|
||||||
|
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is set to `False`.
|
||||||
|
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting is configured to ensure they must wait in the lobby.
|
||||||
|
# - Condition C: Verification using the UI confirms that the setting `Anonymous users and dial-in callers can start a meeting` is set to `Off`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`.
|
||||||
|
# - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby.
|
||||||
|
# - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`.
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
|
# Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings
|
||||||
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
|
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
|
||||||
|
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($anonymousStartDisabled) {
|
$failureReasons = if ($anonymousStartDisabled) {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"Anonymous users and dial-in callers can start a meeting"
|
"Anonymous users and dial-in callers can start a meeting" # Condition A and B
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)" # Condition C
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $anonymousStartDisabled
|
||||||
|
Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "8.5.2"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $anonymousStartDisabled
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-NoWhitelistDomains {
|
function Test-NoWhitelistDomains {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -8,40 +9,77 @@ function Test-NoWhitelistDomains {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.2.2"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains (Automated)
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: No mail transport rules whitelist any specific domains.
|
||||||
|
- Condition B: Verification of mail transport rules using PowerShell shows no domains are whitelisted.
|
||||||
|
- Condition C: Manual review in Exchange Online admin center confirms no whitelisted domains in transport rules.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: One or more mail transport rules whitelist specific domains.
|
||||||
|
- Condition B: Verification of mail transport rules using PowerShell shows one or more domains are whitelisted.
|
||||||
|
- Condition C: Manual review in Exchange Online admin center shows whitelisted domains in transport rules.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
try {
|
||||||
|
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
|
||||||
|
|
||||||
# Retrieve transport rules that whitelist specific domains
|
# Retrieve transport rules that whitelist specific domains
|
||||||
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
|
# Condition A: Checking for transport rules that whitelist specific domains
|
||||||
|
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs }
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($whitelistedRules) {
|
# Condition B: Prepare failure reasons based on the presence of whitelisted rules
|
||||||
"There are transport rules whitelisting specific domains."
|
$failureReasons = if ($whitelistedRules) {
|
||||||
}
|
"There are transport rules whitelisting specific domains."
|
||||||
else {
|
}
|
||||||
"N/A"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$details = if ($whitelistedRules) {
|
# Condition C: Prepare details about the whitelisted rules if found
|
||||||
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
|
$details = if ($whitelistedRules) {
|
||||||
"Whitelisted Rules: $($ruleDetails -join '; ')"
|
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
|
||||||
}
|
"Whitelisted Rules: $($ruleDetails -join '; ')"
|
||||||
else {
|
}
|
||||||
"No transport rules whitelisting specific domains found."
|
else {
|
||||||
}
|
"No transport rules whitelisting specific domains found."
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "6.2.2"
|
Rec = $recnum
|
||||||
Result = -not $whitelistedRules
|
Result = -not $whitelistedRules
|
||||||
Status = if ($whitelistedRules) { "Fail" } else { "Pass" }
|
Status = if ($whitelistedRules) { "Fail" } else { "Pass" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,56 +1,90 @@
|
|||||||
function Test-NotifyMalwareInternal {
|
function Test-NotifyMalwareInternal {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
<#
|
||||||
|
# Conditions for 2.1.3 (L1) Ensure notifications for internal users sending malware is Enabled
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Notifications for internal users sending malware are enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
|
# - Condition B: Using PowerShell, the `NotifyInternal` property in the anti-malware policy is set to `True` and includes at least one valid email address for notifications.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Notifications for internal users sending malware are not enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
|
# - Condition B: Using PowerShell, the `NotifyInternal` property in the anti-malware policy is set to `False` or does not include any valid email addresses for notifications.
|
||||||
|
#>
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
try {
|
||||||
|
# 2.1.3 Ensure notifications for internal users sending malware is Enabled
|
||||||
|
|
||||||
# Retrieve all 'Custom' malware filter policies and check notification settings
|
# Retrieve all 'Custom' malware filter policies and check notification settings
|
||||||
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
|
||||||
$policiesToReport = @()
|
|
||||||
|
|
||||||
foreach ($policy in $malwareNotifications) {
|
# Condition B: Using PowerShell, the `NotifyInternal` property in the anti-malware policy is set to `True` and includes at least one valid email address for notifications.
|
||||||
if ($policy.EnableInternalSenderAdminNotifications -ne $true) {
|
$policiesToReport = @()
|
||||||
$policiesToReport += "$($policy.Identity): Notifications Disabled"
|
foreach ($policy in $malwareNotifications) {
|
||||||
|
if ($policy.EnableInternalSenderAdminNotifications -ne $true) {
|
||||||
|
$policiesToReport += "$($policy.Identity): Notifications Disabled"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Determine the result based on the presence of custom policies without notifications
|
# Determine the result based on the presence of custom policies without notifications
|
||||||
$result = $policiesToReport.Count -eq 0
|
$result = $policiesToReport.Count -eq 0
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($result) {
|
$failureReasons = if ($result) {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"Some custom policies do not have notifications for internal users sending malware enabled."
|
# Condition A: Notifications for internal users sending malware are not enabled in the Microsoft 365 Security & Compliance Center.
|
||||||
}
|
"Some custom policies do not have notifications for internal users sending malware enabled."
|
||||||
|
}
|
||||||
|
|
||||||
$details = if ($result) {
|
$details = if ($result) {
|
||||||
"All custom malware policies have notifications enabled."
|
"All custom malware policies have notifications enabled."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"Misconfigured Policies: $($policiesToReport -join ', ')"
|
"Misconfigured Policies: $($policiesToReport -join ', ')"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "2.1.3"
|
Rec = $recnum
|
||||||
Result = $result
|
Result = $result
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,47 +1,79 @@
|
|||||||
function Test-OneDriveContentRestrictions {
|
function Test-OneDriveContentRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The OneDriveSharingCapability setting is configured to "Disabled" using the PowerShell cmdlet `Get-SPOTenant | fl OneDriveSharingCapability`.
|
||||||
|
# - Condition B: The OneDriveSharingCapability is set to "Only people in your organization" in the SharePoint admin center under Policies > Sharing > OneDrive.
|
||||||
|
# - Condition C: OneDrive content sharing is not more permissive than SharePoint content sharing.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The OneDriveSharingCapability setting is not configured to "Disabled" using the PowerShell cmdlet `Get-SPOTenant | fl OneDriveSharingCapability`.
|
||||||
|
# - Condition B: The OneDriveSharingCapability is not set to "Only people in your organization" in the SharePoint admin center under Policies > Sharing > OneDrive.
|
||||||
|
# - Condition C: OneDrive content sharing is more permissive than SharePoint content sharing.
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
try {
|
||||||
|
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
|
||||||
|
|
||||||
# Retrieve OneDrive sharing capability settings
|
# Retrieve OneDrive sharing capability settings
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
|
||||||
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $isOneDriveSharingRestricted) {
|
$failureReasons = if (-not $isOneDriveSharingRestricted) {
|
||||||
"OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)"
|
"OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = if ($isOneDriveSharingRestricted) {
|
$details = if ($isOneDriveSharingRestricted) {
|
||||||
"OneDrive content sharing is restricted."
|
"OneDrive content sharing is restricted."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
"OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "7.2.4"
|
Rec = $recnum
|
||||||
Result = $isOneDriveSharingRestricted
|
Result = $isOneDriveSharingRestricted
|
||||||
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-OneDriveSyncRestrictions {
|
function Test-OneDriveSyncRestrictions {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -9,39 +10,70 @@ function Test-OneDriveSyncRestrictions {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.3.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
|
try {
|
||||||
|
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Verify that "Allow syncing only on computers joined to specific domains" is enabled.
|
||||||
|
# - Condition B: Check that "TenantRestrictionEnabled" is set to True.
|
||||||
|
# - Condition C: Ensure that "AllowedDomainList" contains the trusted domain GUIDs from the on-premises environment.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: "Allow syncing only on computers joined to specific domains" is not enabled.
|
||||||
|
# - Condition B: "TenantRestrictionEnabled" is set to False.
|
||||||
|
# - Condition C: "AllowedDomainList" does not contain the trusted domain GUIDs from the on-premises environment.
|
||||||
|
|
||||||
# Retrieve OneDrive sync client restriction settings
|
# Retrieve OneDrive sync client restriction settings
|
||||||
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
|
||||||
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Condition A: Check if TenantRestrictionEnabled is True
|
||||||
$failureReasons = if (-not $isSyncRestricted) {
|
# Condition B: Ensure AllowedDomainList contains trusted domains GUIDs
|
||||||
"OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs."
|
$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"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$details = if ($isSyncRestricted) {
|
# Condition C: Prepare details based on whether sync is restricted
|
||||||
"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 ', ')"
|
else {
|
||||||
}
|
"TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "7.3.2"
|
Rec = $recnum
|
||||||
Result = $isSyncRestricted
|
Result = $isSyncRestricted
|
||||||
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-OrgOnlyBypassLobby {
|
function Test-OrgOnlyBypassLobby {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -9,41 +10,73 @@ function Test-OrgOnlyBypassLobby {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
|
try {
|
||||||
|
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is set to `EveryoneInCompanyExcludingGuests`.
|
||||||
|
# - Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
|
||||||
|
# - Condition C: Verification using the Microsoft Teams admin center confirms that the meeting join & lobby settings are configured as recommended.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
|
||||||
|
# - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI.
|
||||||
|
# - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
# Retrieve the Teams meeting policy for lobby bypass settings
|
# Retrieve the Teams meeting policy for lobby bypass settings
|
||||||
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
|
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
|
||||||
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
|
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $lobbyBypassRestricted) {
|
$failureReasons = if (-not $lobbyBypassRestricted) {
|
||||||
"External participants can bypass the lobby"
|
# Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`.
|
||||||
}
|
"External participants can bypass the lobby"
|
||||||
else {
|
}
|
||||||
"N/A"
|
else {
|
||||||
}
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
$details = if ($lobbyBypassRestricted) {
|
$details = if ($lobbyBypassRestricted) {
|
||||||
"Only people in the organization can bypass the lobby."
|
# Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI.
|
||||||
}
|
"Only people in the organization can bypass the lobby."
|
||||||
else {
|
}
|
||||||
"AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
|
else {
|
||||||
}
|
# Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended.
|
||||||
|
"AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "8.5.3"
|
Rec = $recnum
|
||||||
Result = $lobbyBypassRestricted
|
Result = $lobbyBypassRestricted
|
||||||
Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
|
Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-OrganizersPresent {
|
function Test-OrganizersPresent {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -9,41 +10,68 @@ function Test-OrganizersPresent {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.5.6"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
|
try {
|
||||||
|
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is set to `OrganizerOnlyUserOverride`.
|
||||||
|
# - Condition B: Verification using the Teams admin center confirms that the setting "Who can present" is configured to "Only organizers and co-organizers".
|
||||||
|
# - Condition C: Verification using PowerShell confirms that the `DesignatedPresenterRoleMode` is set to `OrganizerOnlyUserOverride`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`.
|
||||||
|
# - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers".
|
||||||
|
# - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`.
|
||||||
|
|
||||||
# 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'
|
||||||
|
|
||||||
# Retrieve the Teams meeting policy for presenters
|
# Prepare failure reasons and details based on compliance
|
||||||
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
|
$failureReasons = if (-not $presenterRoleRestricted) {
|
||||||
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
|
"Others besides organizers and co-organizers can present"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
$details = if ($presenterRoleRestricted) {
|
||||||
$failureReasons = if (-not $presenterRoleRestricted) {
|
"Only organizers and co-organizers can present."
|
||||||
"Others besides organizers and co-organizers can present"
|
}
|
||||||
}
|
else {
|
||||||
else {
|
"DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
|
||||||
"N/A"
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($presenterRoleRestricted) {
|
# Create and populate the CISAuditResult object
|
||||||
"Only organizers and co-organizers can present."
|
$params = @{
|
||||||
}
|
Rec = $recnum
|
||||||
else {
|
Result = $presenterRoleRestricted
|
||||||
"DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
|
Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Retrieve the description from the test definitions
|
||||||
$params = @{
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
Rec = "8.5.6"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
Result = $presenterRoleRestricted
|
|
||||||
Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,43 +1,74 @@
|
|||||||
function Test-PasswordHashSync {
|
function Test-PasswordHashSync {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Conditions for 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Password hash sync is enabled in the Azure AD Connect tool on the on-premises server.
|
||||||
|
# - Condition B: Password hash sync is verified as enabled in the Azure AD Connect Sync section in the Microsoft Entra admin center.
|
||||||
|
# - Condition C: Using Microsoft Graph PowerShell, the verification command returns the expected result indicating that password sync is enabled for the on-premises AD.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Password hash sync is not enabled in the Azure AD Connect tool on the on-premises server.
|
||||||
|
# - Condition B: Password hash sync is not verified as enabled in the Azure AD Connect Sync section in the Microsoft Entra admin center.
|
||||||
|
# - Condition C: Using Microsoft Graph PowerShell, the verification command returns no result indicating that password sync is not enabled for the on-premises AD.
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "5.1.8.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
try {
|
||||||
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
|
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
|
||||||
|
|
||||||
# Retrieve password hash sync status
|
# Retrieve password hash sync status (Condition A and C)
|
||||||
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
|
$passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled
|
||||||
$hashSyncResult = $passwordHashSync
|
$hashSyncResult = $passwordHashSync
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $hashSyncResult) {
|
$failureReasons = if (-not $hashSyncResult) {
|
||||||
"Password hash sync for hybrid deployments is not enabled"
|
"Password hash sync for hybrid deployments is not enabled"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "OnPremisesSyncEnabled: $($passwordHashSync)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $hashSyncResult
|
||||||
|
Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "OnPremisesSyncEnabled: $($passwordHashSync)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "5.1.8.1"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $hashSyncResult
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,43 +1,98 @@
|
|||||||
function Test-PasswordNeverExpirePolicy {
|
function Test-PasswordNeverExpirePolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
[Parameter(Mandatory = $false)]
|
||||||
[Parameter(Mandatory)]
|
[string]$DomainName
|
||||||
[string]$DomainName # DomainName parameter is now mandatory
|
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# .TODO add supported services to output details. ({Email, OfficeCommunicationsOnline, Intune})
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "1.3.1"
|
||||||
|
$overallResult = $true
|
||||||
|
$detailsList = @()
|
||||||
|
$failureReasonsList = @()
|
||||||
|
|
||||||
|
# Add headers for the details
|
||||||
|
$detailsList += "Domain|Validity Period|IsDefault"
|
||||||
|
|
||||||
|
# Conditions for 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire (recommended)'
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Password expiration policy is set to "Set passwords to never expire" in the Microsoft 365 admin center.
|
||||||
|
# - Condition B: Using Microsoft Graph PowerShell, the `PasswordPolicies` property for all users is set to `DisablePasswordExpiration`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Password expiration policy is not set to "Set passwords to never expire" in the Microsoft 365 admin center.
|
||||||
|
# - Condition B: Using Microsoft Graph PowerShell, the `PasswordPolicies` property for one or more users is not set to `DisablePasswordExpiration`.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
|
try {
|
||||||
# Pass if PasswordValidityPeriodInDays is 0. Fail otherwise.
|
# Step: Retrieve all domains or a specific domain
|
||||||
|
$domains = if ($DomainName) {
|
||||||
|
Get-MgDomain -DomainId $DomainName
|
||||||
|
} else {
|
||||||
|
Get-MgDomain
|
||||||
|
}
|
||||||
|
|
||||||
# Retrieve password expiration policy
|
foreach ($domain in $domains) {
|
||||||
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object -ExpandProperty PasswordValidityPeriodInDays
|
$domainName = $domain.Id
|
||||||
|
$isDefault = $domain.IsDefault
|
||||||
|
# Step (Condition A): Retrieve password expiration policy
|
||||||
|
$passwordPolicy = $domain.PasswordValidityPeriodInDays
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Step (Condition A & B): Determine if the policy is compliant
|
||||||
$failureReasons = if ($passwordPolicy -ne 0) {
|
$isCompliant = $passwordPolicy -eq 0
|
||||||
"Password expiration is not set to never expire"
|
$overallResult = $overallResult -and $isCompliant
|
||||||
}
|
|
||||||
else {
|
# Step (Condition A & B): Prepare failure reasons and details based on compliance
|
||||||
"N/A"
|
$failureReasons = if ($isCompliant) {
|
||||||
|
"N/A"
|
||||||
|
} else {
|
||||||
|
"Password expiration is not set to never expire for domain $domainName. Run the following command to remediate: `nUpdate-MgDomain -DomainId $domainName -PasswordValidityPeriodInDays 2147483647 -PasswordNotificationWindowInDays 30`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "$domainName|$passwordPolicy days|$isDefault"
|
||||||
|
|
||||||
|
# Add details and failure reasons to the lists
|
||||||
|
$detailsList += $details
|
||||||
|
$failureReasonsList += $failureReasons
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare the final failure reason and details
|
||||||
|
$finalFailureReason = $failureReasonsList -join "`n"
|
||||||
|
$finalDetails = $detailsList -join "`n"
|
||||||
|
|
||||||
|
# Step: Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $overallResult
|
||||||
|
Status = if ($overallResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $finalDetails
|
||||||
|
FailureReason = $finalFailureReason
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "Validity Period: $passwordPolicy days"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "1.3.1"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $passwordPolicy -eq 0
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($passwordPolicy -eq 0) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,42 +1,74 @@
|
|||||||
function Test-ReauthWithCode {
|
function Test-ReauthWithCode {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
<#
|
||||||
|
Conditions for 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `EmailAttestationRequired` property is set to `True`.
|
||||||
|
# - Condition B: The `EmailAttestationReAuthDays` property is set to `15` or less.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `EmailAttestationRequired` property is set to `False`.
|
||||||
|
# - Condition B: The `EmailAttestationReAuthDays` property is set to more than `15`.
|
||||||
|
#>
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.10"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
try {
|
||||||
|
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
|
||||||
|
|
||||||
# Retrieve reauthentication settings for SharePoint Online
|
# Retrieve reauthentication settings for SharePoint Online
|
||||||
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
|
||||||
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $isReauthenticationRestricted) {
|
$failureReasons = if (-not $isReauthenticationRestricted) {
|
||||||
"Reauthentication with verification code does not require reauthentication within 15 days or less."
|
"Reauthentication with verification code does not require reauthentication within 15 days or less."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"N/A"
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isReauthenticationRestricted
|
||||||
|
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "7.2.10"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $isReauthenticationRestricted
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-ReportSecurityInTeams {
|
function Test-ReportSecurityInTeams {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -8,49 +9,70 @@ function Test-ReportSecurityInTeams {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.6.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.6.1 (L1) Ensure users can report security concerns in Teams
|
try {
|
||||||
|
# 8.6.1 (L1) Ensure users can report security concerns in Teams
|
||||||
|
|
||||||
# Retrieve the necessary settings for Teams and Exchange Online
|
# Retrieve the necessary settings for Teams and Exchange Online
|
||||||
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
|
# Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'.
|
||||||
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
|
||||||
|
|
||||||
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and
|
# Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal.
|
||||||
$ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and
|
# Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses.
|
||||||
$ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and
|
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
|
||||||
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
|
||||||
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Check if all the required settings are enabled
|
||||||
$failureReasons = if (-not $securityReportEnabled) {
|
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and
|
||||||
"Users cannot report security concerns in Teams due to one or more incorrect settings"
|
$ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and
|
||||||
}
|
$ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and
|
||||||
else {
|
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
|
||||||
"N/A"
|
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
|
||||||
|
|
||||||
|
# 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)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $securityReportEnabled
|
||||||
|
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
|
# Retrieve the description from the test definitions
|
||||||
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
|
|
||||||
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "8.6.1"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $securityReportEnabled
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return auditResult
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,60 +1,139 @@
|
|||||||
function Test-RestrictCustomScripts {
|
function Test-RestrictCustomScripts {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
# . .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.3.4"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.3.4 (L1) Ensure custom script execution is restricted on site collections
|
try {
|
||||||
|
# 7.3.4 (L1) Ensure custom script execution is restricted on site collections
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DenyAddAndCustomizePages` setting is set to `Enabled` for each site collection.
|
||||||
|
# - Condition B: The setting is validated through PowerShell commands ensuring the correct state.
|
||||||
|
# - Condition C: Verification using the SharePoint Admin Center confirms the `DenyAddAndCustomizePages` setting is enforced.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `DenyAddAndCustomizePages` setting is not set to `Enabled` for any site collection.
|
||||||
|
# - Condition B: The setting is not validated through PowerShell commands, indicating misconfiguration.
|
||||||
|
# - Condition C: Verification using the SharePoint Admin Center indicates that the `DenyAddAndCustomizePages` setting is not enforced.
|
||||||
|
|
||||||
# Retrieve all site collections and select necessary properties
|
# Retrieve all site collections and select necessary properties
|
||||||
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
|
||||||
|
|
||||||
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
|
# Process URLs to replace 'sharepoint.com' with '<SPUrl>'
|
||||||
$customScriptAllowedSites = $SPOSitesCustomScript | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
|
$processedUrls = $SPOSitesCustomScript | ForEach-Object {
|
||||||
|
$_.Url = $_.Url -replace 'sharepoint\.com', '<SPUrl>'
|
||||||
|
$_
|
||||||
|
}
|
||||||
|
|
||||||
# Compliance is true if no sites allow custom scripts
|
# Find sites where custom scripts are allowed
|
||||||
$complianceResult = $customScriptAllowedSites.Count -eq 0
|
$customScriptAllowedSites = $processedUrls | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
|
||||||
|
#$verbosePreference = 'Continue'
|
||||||
|
# Check the total length of URLs
|
||||||
|
$totalUrlLength = ($customScriptAllowedSites.Url -join '').Length
|
||||||
|
Write-Verbose "Total length of URLs: $totalUrlLength"
|
||||||
|
|
||||||
# Gather details for non-compliant sites (where custom scripts are allowed)
|
# Extract hostnames from allowed sites if the total length exceeds the limit
|
||||||
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object {
|
$mostUsedHostname = $null
|
||||||
"$($_.Title) ($($_.Url)): Custom Script Allowed"
|
if ($totalUrlLength -gt 20000) {
|
||||||
|
Write-Verbose "Extracting hostnames from URLs..."
|
||||||
|
$hostnames = $customScriptAllowedSites.Url | ForEach-Object {
|
||||||
|
if ($_ -match '^https://([^\.]+)\.') {
|
||||||
|
$matches[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Verbose "Extracted hostnames: $($hostnames -join ', ')"
|
||||||
|
|
||||||
|
# Find the most used hostname using the Get-MostCommonWord function
|
||||||
|
$mostUsedHostname = Get-MostCommonWord -InputStrings $hostnames
|
||||||
|
Write-Verbose "Most used hostname: $mostUsedHostname"
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
|
# Compliance is true if no sites allow custom scripts
|
||||||
|
$complianceResult = $customScriptAllowedSites.Count -eq 0
|
||||||
|
|
||||||
|
# Gather details for non-compliant sites (where custom scripts are allowed)
|
||||||
|
$nonCompliantSiteDetails = $customScriptAllowedSites | ForEach-Object {
|
||||||
|
$url = $_.Url
|
||||||
|
if ($null -ne $mostUsedHostname -and $url -match "^https://$mostUsedHostname\.<SPUrl>") {
|
||||||
|
$url = $url -replace "^https://$mostUsedHostname\.<SPUrl>", "https://<corp>.<SPUrl>"
|
||||||
|
}
|
||||||
|
"$(if ($_.Title) {$_.Title} else {"NoTitle"})|$url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prepare failure reasons and details based on compliance
|
||||||
|
$failureReasons = if (-not $complianceResult) {
|
||||||
|
"Some site collections are not restricting custom script execution. Review Details property for sites that are not aligned with the benchmark."
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = if ($complianceResult) {
|
||||||
|
"All site collections have custom script execution restricted"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"Title|Url`n" + ($nonCompliantSiteDetails -join "`n")
|
||||||
|
}
|
||||||
|
|
||||||
|
# Convert details to PSObject and check length
|
||||||
|
$detailsPSObject = $details | ConvertFrom-Csv -Delimiter '|'
|
||||||
|
$detailsLength = ($detailsPSObject | ForEach-Object { $_.Url }).Length
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
# Create a preview of the first 10 results
|
||||||
|
$preview = $detailsPSObject | Select-Object -First 10 | ForEach-Object { "$($_.Title)|$($_.Url)" }
|
||||||
|
$details = "The output is too large. Here is a preview of the first 10 results:`n`n" + ($preview -join "`n") + "`n`nPlease run the test with the following commands to get the full details:`n`nGet-SPOSite -Limit All | Where-Object { `$.DenyAddAndCustomizePages -ne 'Enabled' } | Select-Object Title, Url"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $complianceResult
|
||||||
|
Status = if ($complianceResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Retrieve the description from the test definitions
|
||||||
$failureReasons = if (-not $complianceResult) {
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
}
|
|
||||||
else {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
|
|
||||||
$details = if ($complianceResult) {
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
"All site collections have custom script execution restricted"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$nonCompliantSiteDetails -join "; "
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
$params = @{
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Rec = "7.3.4"
|
|
||||||
Result = $complianceResult
|
|
||||||
Status = if ($complianceResult) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
# Measure the character count of the details
|
||||||
|
#$verbosePreference = 'Continue'
|
||||||
|
$detailsLength = $details.Length
|
||||||
|
Write-Verbose "Character count of the details: $detailsLength"
|
||||||
|
|
||||||
|
if ($detailsLength -gt 32767) {
|
||||||
|
Write-Verbose "Warning: The character count exceeds the limit for Excel cells."
|
||||||
|
}
|
||||||
|
#$verbosePreference = 'SilentlyContinue'
|
||||||
# Return auditResult
|
# Return auditResult
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
|
@@ -1,42 +1,79 @@
|
|||||||
function Test-RestrictExternalSharing {
|
function Test-RestrictExternalSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
<#
|
||||||
|
Conditions for 7.2.3 (L1) Ensure external content sharing is restricted
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: The SharingCapability is set to "ExternalUserSharingOnly" or more restrictive in the SharePoint admin center.
|
||||||
|
- Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "ExternalUserSharingOnly", "ExistingExternalUserSharingOnly", or "Disabled".
|
||||||
|
- Condition C: The external sharing settings in SharePoint Online and OneDrive are set to the same or a more restrictive level than the organization’s sharing settings.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: The SharingCapability is set to "Anyone" or "ExternalUserAndGuestSharing" in the SharePoint admin center.
|
||||||
|
- Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "Anyone" or "ExternalUserAndGuestSharing".
|
||||||
|
- Condition C: The external sharing settings in SharePoint Online and OneDrive are set to a more permissive level than the organization’s sharing settings.
|
||||||
|
#>
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.3 (L1) Ensure external content sharing is restricted
|
try {
|
||||||
|
# 7.2.3 (L1) Ensure external content sharing is restricted
|
||||||
|
|
||||||
# Retrieve the SharingCapability setting for the SharePoint tenant
|
# Retrieve the SharingCapability setting for the SharePoint tenant
|
||||||
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
|
||||||
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if (-not $isRestricted) {
|
# Condition B: Using PowerShell, the SharingCapability property for the SharePoint tenant is set to "ExternalUserSharingOnly", "ExistingExternalUserSharingOnly", or "Disabled".
|
||||||
"External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)"
|
$failureReasons = if (-not $isRestricted) {
|
||||||
}
|
"External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)"
|
||||||
else {
|
}
|
||||||
"N/A"
|
else {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Condition A: The SharingCapability is set to "ExternalUserSharingOnly" or more restrictive in the SharePoint admin center.
|
||||||
|
# Condition C: The external sharing settings in SharePoint Online and OneDrive are set to the same or a more restrictive level than the organization’s sharing settings.
|
||||||
|
$details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isRestricted
|
||||||
|
Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "7.2.3"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $isRestricted
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($isRestricted) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-RestrictOutlookAddins {
|
function Test-RestrictOutlookAddins {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters could include credentials or other necessary data
|
# Parameters could include credentials or other necessary data
|
||||||
@@ -8,67 +9,101 @@ function Test-RestrictOutlookAddins {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code
|
# Initialization code
|
||||||
$customPolicyFailures = @()
|
$customPolicyFailures = @()
|
||||||
$defaultPolicyFailureDetails = @()
|
$defaultPolicyFailureDetails = @()
|
||||||
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
|
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
|
||||||
|
$recnum = "6.3.1"
|
||||||
|
|
||||||
|
# Conditions for 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Verify that the roles MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are unchecked under Other roles.
|
||||||
|
# - Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: One or more of the roles MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are checked under Other roles.
|
||||||
|
# - Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are assigned to users.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
try {
|
||||||
|
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
|
||||||
|
|
||||||
# Check all mailboxes for custom policies with unallowed add-ins
|
# Check all mailboxes for custom policies with unallowed add-ins
|
||||||
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
|
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
|
||||||
|
|
||||||
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
|
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
|
||||||
foreach ($policy in $roleAssignmentPolicies) {
|
foreach ($policy in $roleAssignmentPolicies) {
|
||||||
if ($policy.RoleAssignmentPolicy) {
|
if ($policy.RoleAssignmentPolicy) {
|
||||||
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
|
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
|
||||||
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
||||||
if ($foundRoles) {
|
|
||||||
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
|
# Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users.
|
||||||
|
if ($foundRoles) {
|
||||||
|
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Check Default Role Assignment Policy
|
# Check Default Role Assignment Policy
|
||||||
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
|
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
|
||||||
$defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
$defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles }
|
||||||
if ($defaultPolicyRoles) {
|
|
||||||
$defaultPolicyFailureDetails = $defaultPolicyRoles
|
|
||||||
}
|
|
||||||
|
|
||||||
# Prepare result details string
|
# Condition A: Verify that the roles MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are unchecked under Other roles.
|
||||||
$detailsString = ""
|
if ($defaultPolicyRoles) {
|
||||||
if ($customPolicyFailures) {
|
$defaultPolicyFailureDetails = $defaultPolicyRoles
|
||||||
$detailsString += "Custom Policy Failures: | "
|
}
|
||||||
$detailsString += ($customPolicyFailures -join " | ")
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$detailsString += "Custom Policy Failures: None | "
|
|
||||||
}
|
|
||||||
|
|
||||||
$detailsString += "Default Role Assignment Policy: "
|
# Prepare result details string
|
||||||
if ($defaultPolicyFailureDetails) {
|
$detailsString = ""
|
||||||
$detailsString += ($defaultPolicyFailureDetails -join ', ')
|
if ($customPolicyFailures) {
|
||||||
}
|
$detailsString += "Custom Policy Failures: | "
|
||||||
else {
|
$detailsString += ($customPolicyFailures -join " | ")
|
||||||
$detailsString += "Compliant"
|
}
|
||||||
}
|
else {
|
||||||
|
$detailsString += "Custom Policy Failures: None | "
|
||||||
|
}
|
||||||
|
|
||||||
# Determine result based on findings
|
$detailsString += "Default Role Assignment Policy: "
|
||||||
$isCompliant = -not ($customPolicyFailures -or $defaultPolicyFailureDetails)
|
if ($defaultPolicyFailureDetails) {
|
||||||
|
$detailsString += ($defaultPolicyFailureDetails -join ', ')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$detailsString += "Compliant"
|
||||||
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Determine result based on findings
|
||||||
$params = @{
|
$isCompliant = -not ($customPolicyFailures -or $defaultPolicyFailureDetails)
|
||||||
Rec = "6.3.1"
|
|
||||||
Result = $isCompliant
|
# Create and populate the CISAuditResult object
|
||||||
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
$params = @{
|
||||||
Details = $detailsString
|
Rec = $recnum
|
||||||
FailureReason = if ($isCompliant) { "N/A" } else { "Unauthorized Outlook add-ins found in custom or default policies." }
|
Result = $isCompliant
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = $detailsString
|
||||||
|
FailureReason = if ($isCompliant) { "N/A" } else { "Unauthorized Outlook add-ins found in custom or default policies." }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,50 +1,83 @@
|
|||||||
function Test-RestrictStorageProvidersOutlook {
|
function Test-RestrictStorageProvidersOutlook {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added here if needed
|
# Parameters can be added here if needed
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
<#
|
||||||
|
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Using PowerShell, verify that `AdditionalStorageProvidersAvailable` is set to `False` in the OwaMailboxPolicy.
|
||||||
|
# - Condition B: Ensure that the command `Get-OwaMailboxPolicy | Format-Table Name, AdditionalStorageProvidersAvailable` returns `False`.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Using PowerShell, verify that `AdditionalStorageProvidersAvailable` is not set to `False` in the OwaMailboxPolicy.
|
||||||
|
# - Condition B: Ensure that the command `Get-OwaMailboxPolicy | Format-Table Name, AdditionalStorageProvidersAvailable` does not return `False`.
|
||||||
|
#>
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "6.5.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
try {
|
||||||
|
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
|
||||||
|
|
||||||
# Retrieve all OwaMailbox policies
|
# Retrieve all OwaMailbox policies
|
||||||
$owaPolicies = Get-OwaMailboxPolicy
|
$owaPolicies = Get-OwaMailboxPolicy
|
||||||
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable }
|
# Condition A: Check if AdditionalStorageProvidersAvailable is set to False
|
||||||
|
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable }
|
||||||
|
|
||||||
# Determine compliance
|
# Determine compliance
|
||||||
$allPoliciesRestricted = $nonCompliantPolicies.Count -eq 0
|
$allPoliciesRestricted = $nonCompliantPolicies.Count -eq 0
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($allPoliciesRestricted) {
|
$failureReasons = if ($allPoliciesRestricted) {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable."
|
"One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable."
|
||||||
}
|
}
|
||||||
|
|
||||||
$details = if ($allPoliciesRestricted) {
|
$details = if ($allPoliciesRestricted) {
|
||||||
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
|
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies.Name -join ', ')"
|
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies.Name -join ', ')"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
# Create and populate the CISAuditResult object
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "6.5.3"
|
Rec = $recnum
|
||||||
Result = $allPoliciesRestricted
|
Result = $allPoliciesRestricted
|
||||||
Status = if ($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
Status = if ($allPoliciesRestricted) { "Pass" } else { "Fail" }
|
||||||
Details = $details
|
Details = $details
|
||||||
FailureReason = $failureReasons
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
@@ -52,5 +85,3 @@ function Test-RestrictStorageProvidersOutlook {
|
|||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Additional helper functions (if any)
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-RestrictTenantCreation {
|
function Test-RestrictTenantCreation {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -8,35 +9,67 @@ function Test-RestrictTenantCreation {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "5.1.2.3"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Restrict non-admin users from creating tenants is set to 'Yes' in the Azure AD and Entra administration portal.
|
||||||
|
- Condition B: Using PowerShell, the setting for restricting non-admin users from creating tenants is set to 'Yes'.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: Restrict non-admin users from creating tenants is not set to 'Yes' in the Azure AD and Entra administration portal.
|
||||||
|
- Condition B: Using PowerShell, the setting for restricting non-admin users from creating tenants is not set to 'Yes'.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
try {
|
||||||
|
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
|
||||||
|
|
||||||
# Retrieve the tenant creation policy
|
# Retrieve the tenant creation policy
|
||||||
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
|
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
|
||||||
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
|
||||||
|
|
||||||
# Prepare failure reasons and details based on compliance
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($tenantCreationResult) {
|
$failureReasons = if ($tenantCreationResult) {
|
||||||
"N/A"
|
"N/A"
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"Non-admin users can create tenants"
|
"Non-admin users can create tenants"
|
||||||
|
}
|
||||||
|
|
||||||
|
$details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $tenantCreationResult
|
||||||
|
Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
$details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "5.1.2.3"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $tenantCreationResult
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
@@ -44,5 +77,3 @@ function Test-RestrictTenantCreation {
|
|||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Additional helper functions (if any)
|
|
||||||
|
@@ -1,47 +1,113 @@
|
|||||||
function Test-SafeAttachmentsPolicy {
|
function Test-SafeAttachmentsPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param (
|
[OutputType([CISAuditResult])]
|
||||||
# Aligned
|
param ()
|
||||||
# Parameters can be added if needed
|
|
||||||
)
|
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.4"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: The Safe Attachments policy is enabled in the Microsoft 365 Defender portal.
|
||||||
|
- Condition B: The policy covers all recipients within the organization.
|
||||||
|
- Condition C: The policy action is set to "Dynamic Delivery" or "Quarantine".
|
||||||
|
- Condition D: The policy is not disabled.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: The Safe Attachments policy is not enabled in the Microsoft 365 Defender portal.
|
||||||
|
- Condition B: The policy does not cover all recipients within the organization.
|
||||||
|
- Condition C: The policy action is not set to "Dynamic Delivery" or "Quarantine".
|
||||||
|
- Condition D: The policy is disabled.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) {
|
||||||
|
try {
|
||||||
|
# Retrieve all Safe Attachment policies where Enable is set to True
|
||||||
|
$safeAttachmentPolicies = Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue | Where-Object { $_.Enable -eq $true }
|
||||||
|
# Check if any Safe Attachments policy is enabled (Condition A)
|
||||||
|
$result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
|
||||||
|
|
||||||
# Retrieve all Safe Attachment policies where Enable is set to True
|
# Initialize details and failure reasons
|
||||||
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
|
$details = @()
|
||||||
|
$failureReasons = @()
|
||||||
|
|
||||||
# Determine result and details based on the presence of enabled policies
|
foreach ($policy in $safeAttachmentPolicies) {
|
||||||
$result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
|
# Initialize policy detail and failed status
|
||||||
$details = if ($result) {
|
$failed = $false
|
||||||
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
|
||||||
|
# Check if the policy action is set to "Dynamic Delivery" or "Quarantine" (Condition C)
|
||||||
|
if ($policy.Action -notin @("DynamicDelivery", "Quarantine")) {
|
||||||
|
$failureReasons += "Policy '$($policy.Name)' action is not set to 'Dynamic Delivery' or 'Quarantine'."
|
||||||
|
$failed = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if the policy is not disabled (Condition D)
|
||||||
|
if (-not $policy.Enable) {
|
||||||
|
$failureReasons += "Policy '$($policy.Name)' is disabled."
|
||||||
|
$failed = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Add policy details to the details array
|
||||||
|
$details += [PSCustomObject]@{
|
||||||
|
Policy = $policy.Name
|
||||||
|
Enabled = $policy.Enable
|
||||||
|
Action = $policy.Action
|
||||||
|
Failed = $failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# The result is a pass if there are no failure reasons
|
||||||
|
$result = $failureReasons.Count -eq 0
|
||||||
|
|
||||||
|
# Format details for output
|
||||||
|
$detailsString = $details | Format-Table -AutoSize | Out-String
|
||||||
|
$failureReasonsString = ($failureReasons | ForEach-Object { $_ }) -join ' '
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $detailsString
|
||||||
|
FailureReason = if ($result) { "N/A" } else { $failureReasonsString }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"No Safe Attachments Policies are enabled."
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $false
|
||||||
|
Status = "Fail"
|
||||||
|
Details = "No M365 E5 licenses found."
|
||||||
|
FailureReason = "The audit is for M365 E5 licenses and the required EXO commands will not be available otherwise."
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
$failureReasons = if ($result) {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
"Safe Attachments policy is not enabled."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
|
||||||
$params = @{
|
|
||||||
Rec = "2.1.4"
|
|
||||||
Result = $result
|
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
@@ -49,5 +115,3 @@ function Test-SafeAttachmentsPolicy {
|
|||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Additional helper functions (if any)
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-SafeAttachmentsTeams {
|
function Test-SafeAttachmentsTeams {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -8,53 +9,97 @@ function Test-SafeAttachmentsTeams {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
|
|
||||||
|
# Conditions for 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Safe Attachments for SharePoint is enabled.
|
||||||
|
# - Condition B: Safe Attachments for OneDrive is enabled.
|
||||||
|
# - Condition C: Safe Attachments for Microsoft Teams is enabled.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Safe Attachments for SharePoint is not enabled.
|
||||||
|
# - Condition B: Safe Attachments for OneDrive is not enabled.
|
||||||
|
# - Condition C: Safe Attachments for Microsoft Teams is not enabled.
|
||||||
|
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.5"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
|
if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) {
|
||||||
|
try {
|
||||||
|
# 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
|
||||||
|
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
|
||||||
|
$atpPolicies = Get-AtpPolicyForO365
|
||||||
|
# Check if the required ATP policies are enabled
|
||||||
|
$atpPolicyResult = $atpPolicies | Where-Object {
|
||||||
|
$_.EnableATPForSPOTeamsODB -eq $true -and
|
||||||
|
$_.EnableSafeDocs -eq $true -and
|
||||||
|
$_.AllowSafeDocsOpen -eq $false
|
||||||
|
}
|
||||||
|
|
||||||
# Retrieve the ATP policies for Office 365 and check Safe Attachments settings
|
# Condition A: Check Safe Attachments for SharePoint
|
||||||
$atpPolicies = Get-AtpPolicyForO365
|
# Condition B: Check Safe Attachments for OneDrive
|
||||||
|
# Condition C: Check Safe Attachments for Microsoft Teams
|
||||||
|
|
||||||
# Check if the required ATP policies are enabled
|
# Determine the result based on the ATP policy settings
|
||||||
$atpPolicyResult = $atpPolicies | Where-Object {
|
$result = $null -ne $atpPolicyResult
|
||||||
$_.EnableATPForSPOTeamsODB -eq $true -and
|
$details = if ($result) {
|
||||||
$_.EnableSafeDocs -eq $true -and
|
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
||||||
$_.AllowSafeDocsOpen -eq $false
|
}
|
||||||
}
|
else {
|
||||||
|
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
||||||
|
}
|
||||||
|
|
||||||
# Determine the result based on the ATP policy settings
|
$failureReasons = if ($result) {
|
||||||
$result = $null -ne $atpPolicyResult
|
"N/A"
|
||||||
$details = if ($result) {
|
}
|
||||||
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
else {
|
||||||
|
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $false
|
||||||
|
Status = "Fail"
|
||||||
|
Details = "No M365 E5 licenses found."
|
||||||
|
FailureReason = "The audit is for M365 E5 licenses and the required EXO commands will not be available otherwise."
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
|
||||||
$failureReasons = if ($result) {
|
|
||||||
"N/A"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
|
||||||
$params = @{
|
|
||||||
Rec = "2.1.5"
|
|
||||||
Result = $result
|
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
# Return the audit result
|
# Return the audit result
|
||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Additional helper functions (if any)
|
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-SafeLinksOfficeApps {
|
function Test-SafeLinksOfficeApps {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here if needed
|
# Define your parameters here if needed
|
||||||
@@ -9,53 +10,102 @@ function Test-SafeLinksOfficeApps {
|
|||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "2.1.1"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: In the Microsoft 365 security center, Safe Links policy for Office applications is enabled and the following protection settings are set:
|
||||||
|
- Office 365 Apps: On
|
||||||
|
- Teams: On
|
||||||
|
- Email: On
|
||||||
|
- Click protection settings: On
|
||||||
|
- Do not track when users click safe links: Off
|
||||||
|
- Condition B: Using the Exchange Online PowerShell Module, Safe Links policies are retrieved, and the relevant policy shows Safe Links for Office applications is enabled.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: In the Microsoft 365 security center, Safe Links policy for Office applications is not enabled or one or more of the required protection settings are not set correctly.
|
||||||
|
- Office 365 Apps: Off
|
||||||
|
- Teams: Off
|
||||||
|
- Email: Off
|
||||||
|
- Click protection settings: Off
|
||||||
|
- Do not track when users click safe links: On
|
||||||
|
- Condition B: Using the Exchange Online PowerShell Module, Safe Links policies are retrieved, and the relevant policy shows Safe Links for Office applications is not enabled.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) {
|
||||||
|
try {
|
||||||
|
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
||||||
|
# Retrieve all Safe Links policies
|
||||||
|
$policies = Get-SafeLinksPolicy
|
||||||
|
# Initialize the details collection
|
||||||
|
$misconfiguredDetails = @()
|
||||||
|
|
||||||
# Retrieve all Safe Links policies
|
foreach ($policy in $policies) {
|
||||||
$policies = Get-SafeLinksPolicy
|
# Get the detailed configuration of each policy
|
||||||
|
$policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
|
||||||
|
|
||||||
# Initialize the details collection
|
# Check each required property and record failures
|
||||||
$misconfiguredDetails = @()
|
# Condition A: Checking policy settings
|
||||||
|
$failures = @()
|
||||||
|
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On
|
||||||
|
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On
|
||||||
|
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On
|
||||||
|
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On
|
||||||
|
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off
|
||||||
|
|
||||||
foreach ($policy in $policies) {
|
# Only add details for policies that have misconfigurations
|
||||||
# Get the detailed configuration of each policy
|
if ($failures.Count -gt 0) {
|
||||||
$policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
|
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# Check each required property and record failures
|
# Prepare the final result
|
||||||
$failures = @()
|
# Condition B: Ensuring no misconfigurations
|
||||||
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" }
|
$result = $misconfiguredDetails.Count -eq 0
|
||||||
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" }
|
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
||||||
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" }
|
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
||||||
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" }
|
|
||||||
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" }
|
|
||||||
if ($policyDetails.ScanUrls -ne $true) { $failures += "ScanUrls: False" }
|
|
||||||
if ($policyDetails.EnableForInternalSenders -ne $true) { $failures += "EnableForInternalSenders: False" }
|
|
||||||
if ($policyDetails.DeliverMessageAfterScan -ne $true) { $failures += "DeliverMessageAfterScan: False" }
|
|
||||||
if ($policyDetails.DisableUrlRewrite -ne $false) { $failures += "DisableUrlRewrite: True" }
|
|
||||||
|
|
||||||
# Only add details for policies that have misconfigurations
|
# Create and populate the CISAuditResult object
|
||||||
if ($failures.Count -gt 0) {
|
$params = @{
|
||||||
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
# Prepare the final result
|
$params = @{
|
||||||
$result = $misconfiguredDetails.Count -eq 0
|
Rec = $recnum
|
||||||
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
Result = $false
|
||||||
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
Status = "Fail"
|
||||||
|
Details = "No M365 E5 licenses found."
|
||||||
# Create and populate the CISAuditResult object
|
FailureReason = "The audit is for M365 E5 licenses and the required EXO commands will not be available otherwise."
|
||||||
$params = @{
|
}
|
||||||
Rec = "2.1.1"
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
Result = $result
|
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
|
||||||
Details = $details
|
|
||||||
FailureReason = $failureReasons
|
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,31 +1,62 @@
|
|||||||
function Test-SharePointAADB2B {
|
function Test-SharePointAADB2B {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
)
|
)
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
|
# Conditions for 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: Ensure the `EnableAzureADB2BIntegration` property is set to `True` for the SharePoint tenant.
|
||||||
|
# - Condition B: Verify that the SharePoint and OneDrive integration with Azure AD B2B is active.
|
||||||
|
# - Condition C: Ensure that guest accounts are managed in Azure AD and subject to access policies.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `EnableAzureADB2BIntegration` property is set to `False` for the SharePoint tenant.
|
||||||
|
# - Condition B: The integration between SharePoint, OneDrive, and Azure AD B2B is not active.
|
||||||
|
# - Condition C: Guest accounts are not managed in Azure AD and are not subject to access policies.
|
||||||
|
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$recnum = "7.2.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
try {
|
||||||
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
# 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled
|
||||||
|
$SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Populate the auditResult object with the required properties
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "7.2.2"
|
Rec = $recnum
|
||||||
Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
|
||||||
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
|
||||||
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
|
||||||
FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-SharePointExternalSharingDomains {
|
function Test-SharePointExternalSharingDomains {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -8,25 +9,55 @@ function Test-SharePointExternalSharingDomains {
|
|||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
# Initialization code, if needed
|
||||||
|
$recnum = "7.2.6"
|
||||||
|
|
||||||
|
# Conditions for 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The "Limit external sharing by domain" option is enabled in the SharePoint admin center.
|
||||||
|
# - Condition B: The "SharingDomainRestrictionMode" is set to "AllowList" using PowerShell.
|
||||||
|
# - Condition C: The "SharingAllowedDomainList" contains the domains trusted by the organization for external sharing.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The "Limit external sharing by domain" option is not enabled in the SharePoint admin center.
|
||||||
|
# - Condition B: The "SharingDomainRestrictionMode" is not set to "AllowList" using PowerShell.
|
||||||
|
# - Condition C: The "SharingAllowedDomainList" does not contain the domains trusted by the organization for external sharing.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
|
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
|
||||||
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
|
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
|
||||||
|
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Populate the auditResult object with the required properties
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "7.2.6"
|
Rec = $recnum
|
||||||
Result = $isDomainRestrictionConfigured
|
Result = $isDomainRestrictionConfigured
|
||||||
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
|
||||||
Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
|
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" }
|
FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-SharePointGuestsItemSharing {
|
function Test-SharePointGuestsItemSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Define your parameters here
|
# Define your parameters here
|
||||||
@@ -10,23 +11,53 @@ function Test-SharePointGuestsItemSharing {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$recnum = "7.2.5"
|
||||||
|
|
||||||
|
# Conditions for 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The SharePoint setting "PreventExternalUsersFromResharing" is set to `True` using PowerShell.
|
||||||
|
# - Condition B: The SharePoint admin center setting "Allow guests to share items they don't own" is unchecked.
|
||||||
|
# - Condition C: Ensure that external users cannot re-share items they don't own.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The SharePoint setting "PreventExternalUsersFromResharing" is set to `False` using PowerShell.
|
||||||
|
# - Condition B: The SharePoint admin center setting "Allow guests to share items they don't own" is checked.
|
||||||
|
# - Condition C: Ensure that external users can re-share items they don't own.
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
try {
|
||||||
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
|
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
|
||||||
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
|
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
|
||||||
|
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
|
||||||
|
|
||||||
# Populate the auditResult object with the required properties
|
# Populate the auditResult object with the required properties
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "7.2.5"
|
Rec = $recnum
|
||||||
Result = $isGuestResharingPrevented
|
Result = $isGuestResharingPrevented
|
||||||
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
|
||||||
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
|
||||||
FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-SpamPolicyAdminNotify {
|
function Test-SpamPolicyAdminNotify {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added if needed
|
# Parameters can be added if needed
|
||||||
@@ -10,38 +11,71 @@ function Test-SpamPolicyAdminNotify {
|
|||||||
#. .\source\Classes\CISAuditResult.ps1
|
#. .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$recnum = "2.1.6"
|
||||||
|
|
||||||
|
<#
|
||||||
|
Conditions for 2.1.6 (L1) Ensure Exchange Online Spam Policies are set to notify administrators
|
||||||
|
|
||||||
|
Validate recommendation details:
|
||||||
|
- Confirm that the recommendation details are accurate and complete as per the CIS benchmark.
|
||||||
|
|
||||||
|
Validate test for a pass:
|
||||||
|
- Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: In the Microsoft 365 Security & Compliance Center, the Exchange Online Spam Policies are set to notify administrators when a sender in the organization has been blocked for sending spam emails.
|
||||||
|
- Condition B: Using PowerShell, the `NotifyOutboundSpam` and `NotifyOutboundSpamContact` properties are correctly set in all relevant spam filter policies.
|
||||||
|
|
||||||
|
Validate test for a fail:
|
||||||
|
- Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: In the Microsoft 365 Security & Compliance Center, the Exchange Online Spam Policies are not set to notify administrators when a sender in the organization has been blocked for sending spam emails.
|
||||||
|
- Condition B: Using PowerShell, the `NotifyOutboundSpam` and `NotifyOutboundSpamContact` properties are not correctly set in all relevant spam filter policies.
|
||||||
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
|
try {
|
||||||
|
# 2.1.6 Ensure Exchange Online Spam Policies are set to notify administrators
|
||||||
|
|
||||||
# Get the default hosted outbound spam filter policy
|
# Retrieve the default hosted outbound spam filter policy
|
||||||
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
|
||||||
|
|
||||||
# Check if both settings are enabled
|
# Check if both settings are enabled (Condition A and Condition B for pass)
|
||||||
$bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail
|
$bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail
|
||||||
$notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
|
$notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
|
||||||
$areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
|
$areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
|
||||||
|
|
||||||
# Prepare failure details if any setting is not enabled
|
# Prepare failure details if any setting is not enabled (Condition A and Condition B for fail)
|
||||||
$failureDetails = @()
|
$failureDetails = @()
|
||||||
if (-not $bccSuspiciousOutboundMailEnabled) {
|
if (-not $bccSuspiciousOutboundMailEnabled) {
|
||||||
$failureDetails += "BccSuspiciousOutboundMail is not enabled."
|
$failureDetails += "BccSuspiciousOutboundMail is not enabled."
|
||||||
}
|
}
|
||||||
if (-not $notifyOutboundSpamEnabled) {
|
if (-not $notifyOutboundSpamEnabled) {
|
||||||
$failureDetails += "NotifyOutboundSpam is not enabled."
|
$failureDetails += "NotifyOutboundSpam is not enabled."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create an instance of CISAuditResult and populate it
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $areSettingsEnabled
|
||||||
|
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
||||||
|
Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
|
||||||
|
FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Retrieve the description from the test definitions
|
||||||
$params = @{
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
Rec = "2.1.6"
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
Result = $areSettingsEnabled
|
|
||||||
Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
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" }
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
@@ -49,3 +83,4 @@ function Test-SpamPolicyAdminNotify {
|
|||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-TeamsExternalAccess {
|
function Test-TeamsExternalAccess {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be defined here if needed
|
# Parameters can be defined here if needed
|
||||||
@@ -7,36 +8,65 @@ function Test-TeamsExternalAccess {
|
|||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
# . .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
|
||||||
$auditResult = [CISAuditResult]::new()
|
$auditResult = [CISAuditResult]::new()
|
||||||
|
$recnum = "8.2.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
|
try {
|
||||||
|
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
|
||||||
|
#
|
||||||
|
# Validate test for a pass:
|
||||||
|
# - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowTeamsConsumer` setting is `False`.
|
||||||
|
# - Condition B: The `AllowPublicUsers` setting is `False`.
|
||||||
|
# - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names.
|
||||||
|
#
|
||||||
|
# Validate test for a fail:
|
||||||
|
# - Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
# - Specific conditions to check:
|
||||||
|
# - Condition A: The `AllowTeamsConsumer` setting is not `False`.
|
||||||
|
# - Condition B: The `AllowPublicUsers` setting is not `False`.
|
||||||
|
# - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly.
|
||||||
|
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
$externalAccessConfig = Get-CsTenantFederationConfiguration
|
$externalAccessConfig = Get-CsTenantFederationConfiguration
|
||||||
|
|
||||||
$allowedDomainsLimited = $false
|
$allowedDomainsLimited = $false
|
||||||
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
|
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
|
||||||
$allowedDomainsLimited = $true
|
$allowedDomainsLimited = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if the configurations are as recommended
|
||||||
|
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
||||||
|
|
||||||
|
# Create an instance of CISAuditResult and populate it
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $isCompliant
|
||||||
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
|
Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
|
||||||
|
FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Check if the configurations are as recommended
|
# Retrieve the description from the test definitions
|
||||||
$isCompliant = -not $externalAccessConfig.AllowTeamsConsumer -and -not $externalAccessConfig.AllowPublicUsers -and (-not $externalAccessConfig.AllowFederatedUsers -or $allowedDomainsLimited)
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$params = @{
|
|
||||||
Rec = "8.2.1"
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
Result = $isCompliant
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
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 {
|
end {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
function Test-TeamsExternalFileSharing {
|
function Test-TeamsExternalFileSharing {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param (
|
||||||
# Aligned
|
# Aligned
|
||||||
# Parameters can be added here if needed
|
# Parameters can be added here if needed
|
||||||
@@ -7,40 +8,59 @@ function Test-TeamsExternalFileSharing {
|
|||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
#. .\source\Classes\CISAuditResult.ps1
|
# . .\source\Classes\CISAuditResult.ps1
|
||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
|
$recnum = "8.1.1"
|
||||||
$auditResult = [CISAuditResult]::new()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
# 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services
|
try {
|
||||||
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
# 8.1.1 (L2) Ensure external file sharing in Teams is enabled for only approved cloud storage services
|
||||||
|
# Connect to Teams PowerShell using Connect-MicrosoftTeams
|
||||||
|
|
||||||
# Assuming that 'approvedProviders' is a list of approved cloud storage service names
|
# Condition A: The `AllowDropbox` setting is set to `False`.
|
||||||
# This list must be defined according to your organization's approved cloud storage services
|
# Condition B: The `AllowBox` setting is set to `False`.
|
||||||
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
|
# Condition C: The `AllowGoogleDrive` setting is set to `False`.
|
||||||
$clientConfig = Get-CsTeamsClientConfiguration
|
# Condition D: The `AllowShareFile` setting is set to `False`.
|
||||||
|
# Condition E: The `AllowEgnyte` setting is set to `False`.
|
||||||
|
|
||||||
$isCompliant = $true
|
# Assuming that 'approvedProviders' is a list of approved cloud storage service names
|
||||||
$nonCompliantProviders = @()
|
# This list must be defined according to your organization's approved cloud storage services
|
||||||
|
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
|
||||||
|
$clientConfig = Get-CsTeamsClientConfiguration
|
||||||
|
|
||||||
foreach ($provider in $approvedProviders) {
|
$isCompliant = $true
|
||||||
if (-not $clientConfig.$provider) {
|
$nonCompliantProviders = @()
|
||||||
$isCompliant = $false
|
|
||||||
$nonCompliantProviders += $provider
|
foreach ($provider in $approvedProviders) {
|
||||||
|
if (-not $clientConfig.$provider) {
|
||||||
|
$isCompliant = $false
|
||||||
|
$nonCompliantProviders += $provider
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
# Create an instance of CISAuditResult and populate it
|
# Create an instance of CISAuditResult and populate it
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = "8.1.1"
|
Rec = $recnum
|
||||||
Result = $isCompliant
|
Result = $isCompliant
|
||||||
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
Status = if ($isCompliant) { "Pass" } else { "Fail" }
|
||||||
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
|
||||||
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
end {
|
end {
|
||||||
|
405
test-gh.ps1
Normal file
405
test-gh.ps1
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
$repoOwner = "CriticalSolutionsNetwork"
|
||||||
|
$repoName = "M365FoundationsCISReport"
|
||||||
|
$directoryPath = ".\source\tests"
|
||||||
|
$projectName = "Test Validation Project"
|
||||||
|
|
||||||
|
# Function to create GitHub issues
|
||||||
|
function Create-GitHubIssue {
|
||||||
|
param (
|
||||||
|
[string]$title,
|
||||||
|
[string]$body,
|
||||||
|
[string]$project
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create the issue and add it to the specified project
|
||||||
|
$issue = gh issue create --repo "$repoOwner/$repoName" --title "$title" --body "$body" --project "$project"
|
||||||
|
return $issue
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load test definitions from CSV
|
||||||
|
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
|
||||||
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
|
|
||||||
|
# Iterate over each .ps1 file in the directory
|
||||||
|
Get-ChildItem -Path $directoryPath -Filter "*.ps1" | ForEach-Object {
|
||||||
|
$fileName = $_.Name
|
||||||
|
$testDefinition = $testDefinitions | Where-Object { $_.TestFileName -eq $fileName }
|
||||||
|
|
||||||
|
if ($testDefinition) {
|
||||||
|
$rec = $testDefinition.Rec
|
||||||
|
$elevel = $testDefinition.ELevel
|
||||||
|
$profileLevel = $testDefinition.ProfileLevel
|
||||||
|
$ig1 = $testDefinition.IG1
|
||||||
|
$ig2 = $testDefinition.IG2
|
||||||
|
$ig3 = $testDefinition.IG3
|
||||||
|
$connection = $testDefinition.Connection
|
||||||
|
|
||||||
|
$issueTitle = "Rec: $rec - Validate $fileName, ELevel: $elevel, ProfileLevel: $profileLevel, IG1: $ig1, IG2: $ig2, IG3: $ig3, Connection: $connection"
|
||||||
|
$issueBody = @"
|
||||||
|
# Validation for $fileName
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
- [ ] Validate test for a pass
|
||||||
|
- Description of passing criteria:
|
||||||
|
- [ ] Validate test for a fail
|
||||||
|
- Description of failing criteria:
|
||||||
|
- [ ] Add notes and observations
|
||||||
|
- Placeholder for additional notes:
|
||||||
|
"@
|
||||||
|
|
||||||
|
# Create the issue using GitHub CLI
|
||||||
|
try {
|
||||||
|
Create-GitHubIssue -title "$issueTitle" -body "$issueBody" -project "$projectName"
|
||||||
|
Write-Output "Created issue for $fileName"
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Failed to create issue for $fileName`: $_"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Introduce a delay of 2 seconds
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Warning "No matching test definition found for $fileName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
######################################
|
||||||
|
$repoOwner = "CriticalSolutionsNetwork"
|
||||||
|
$repoName = "M365FoundationsCISReport"
|
||||||
|
|
||||||
|
# Function to update GitHub issue
|
||||||
|
function Update-GitHubTIssue {
|
||||||
|
param (
|
||||||
|
[int]$issueNumber,
|
||||||
|
[string]$title,
|
||||||
|
[string]$body,
|
||||||
|
[string]$owner,
|
||||||
|
[string]$repositoryName
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update the issue using Set-GitHubIssue
|
||||||
|
Set-GitHubIssue -OwnerName $owner -RepositoryName $repositoryName -Issue $issueNumber -Title $title -Body $body -Label @("documentation", "help wanted", "question") -Confirm:$false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Load test definitions from CSV
|
||||||
|
$testDefinitionsPath = ".\source\helper\TestDefinitions.csv"
|
||||||
|
$testDefinitions = Import-Csv -Path $testDefinitionsPath
|
||||||
|
|
||||||
|
# Fetch existing issues that start with "Rec:"
|
||||||
|
$existingIssues = Get-GitHubIssue -OwnerName 'CriticalSolutionsNetwork' -RepositoryName 'M365FoundationsCISReport'
|
||||||
|
|
||||||
|
# Create a list to hold matched issues
|
||||||
|
$matchedIssues = @()
|
||||||
|
$warnings = @()
|
||||||
|
|
||||||
|
# Iterate over each existing issue
|
||||||
|
$existingIssues | ForEach-Object {
|
||||||
|
$issueNumber = $_.Number
|
||||||
|
$issueTitle = $_.Title
|
||||||
|
$issueBody = $_.Body
|
||||||
|
|
||||||
|
# Extract the rec number from the issue title
|
||||||
|
if ($issueTitle -match "Rec: (\d+\.\d+\.\d+)") {
|
||||||
|
$rec = $matches[1]
|
||||||
|
|
||||||
|
# Find the matching test definition based on rec number
|
||||||
|
$testDefinition = $testDefinitions | Where-Object { $_.Rec -eq $rec }
|
||||||
|
|
||||||
|
if ($testDefinition) {
|
||||||
|
# Create the new issue body
|
||||||
|
$newIssueBody = @"
|
||||||
|
# Validation for $($testDefinition.TestFileName)
|
||||||
|
|
||||||
|
## Recommendation Details
|
||||||
|
- **Recommendation**: $($testDefinition.Rec)
|
||||||
|
- **Description**: $($testDefinition.RecDescription)
|
||||||
|
- **ELevel**: $($testDefinition.ELevel)
|
||||||
|
- **Profile Level**: $($testDefinition.ProfileLevel)
|
||||||
|
- **CIS Control**: $($testDefinition.CISControl)
|
||||||
|
- **CIS Description**: $($testDefinition.CISDescription)
|
||||||
|
- **Implementation Group 1**: $($testDefinition.IG1)
|
||||||
|
- **Implementation Group 2**: $($testDefinition.IG2)
|
||||||
|
- **Implementation Group 3**: $($testDefinition.IG3)
|
||||||
|
- **Automated**: $($testDefinition.Automated)
|
||||||
|
- **Connection**: $($testDefinition.Connection)
|
||||||
|
|
||||||
|
## [$($testDefinition.TestFileName)](https://github.com/CriticalSolutionsNetwork/M365FoundationsCISReport/blob/main/source/tests/$($testDefinition.TestFileName))
|
||||||
|
|
||||||
|
## Tasks
|
||||||
|
|
||||||
|
### Validate recommendation details
|
||||||
|
- [ ] Confirm that the recommendation details are accurate and complete as per the CIS benchmark.
|
||||||
|
|
||||||
|
### Validate test for a pass
|
||||||
|
- [ ] Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: (Detail about what constitutes Condition A)
|
||||||
|
- Condition B: (Detail about what constitutes Condition B)
|
||||||
|
- Condition C: (Detail about what constitutes Condition C)
|
||||||
|
|
||||||
|
### Validate test for a fail
|
||||||
|
- [ ] Confirm that the failure conditions in the automated test are consistent with the manual audit results.
|
||||||
|
- Specific conditions to check:
|
||||||
|
- Condition A: (Detail about what constitutes Condition A)
|
||||||
|
- Condition B: (Detail about what constitutes Condition B)
|
||||||
|
- Condition C: (Detail about what constitutes Condition C)
|
||||||
|
|
||||||
|
### Add notes and observations
|
||||||
|
- [ ] Compare the automated audit results with the manual audit steps and provide detailed observations.
|
||||||
|
- Automated audit produced info consistent with the manual audit test results? (Yes/No)
|
||||||
|
- Without disclosing any sensitive information, document any discrepancies between the actual output and the expected output.
|
||||||
|
- Document any error messages, removing any sensitive information before submitting.
|
||||||
|
- Identify the specific function, line, or section of the script that failed, if known.
|
||||||
|
- Provide any additional context or observations that might help in troubleshooting.
|
||||||
|
|
||||||
|
If needed, the helpers folder in .\source\helpers contains a CSV to assist with locating the test definition.
|
||||||
|
"@
|
||||||
|
|
||||||
|
# Add to matched issues list
|
||||||
|
$matchedIssues += [PSCustomObject]@{
|
||||||
|
IssueNumber = $issueNumber
|
||||||
|
Title = $issueTitle
|
||||||
|
NewBody = $newIssueBody
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$warnings += "No matching test definition found for Rec: $rec"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$warnings += "No matching rec number found in issue title #$issueNumber"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display matched issues for confirmation
|
||||||
|
if ($matchedIssues.Count -gt 0) {
|
||||||
|
Write-Output "Matched Issues:"
|
||||||
|
$matchedIssues | ForEach-Object {
|
||||||
|
Write-Output $_.Title
|
||||||
|
}
|
||||||
|
|
||||||
|
$confirmation = Read-Host "Do you want to proceed with updating these issues? (yes/no)"
|
||||||
|
|
||||||
|
if ($confirmation -eq 'yes') {
|
||||||
|
# Update the issues
|
||||||
|
$matchedIssues | ForEach-Object {
|
||||||
|
try {
|
||||||
|
Update-GitHubTIssue -issueNumber $_.IssueNumber -title $_.Title -body $_.NewBody -owner $repoOwner -repositoryName $repoName
|
||||||
|
Write-Output "Updated issue #$($_.IssueNumber)"
|
||||||
|
} catch {
|
||||||
|
Write-Error "Failed to update issue #$($_.IssueNumber): $_"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Introduce a delay of 2 seconds
|
||||||
|
Start-Sleep -Seconds 2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Output "Update canceled by user."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Output "No matched issues found to update."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display any warnings that were captured
|
||||||
|
if ($warnings.Count -gt 0) {
|
||||||
|
Write-Output "Warnings:"
|
||||||
|
$warnings | ForEach-Object {
|
||||||
|
Write-Output $_
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test command to verify GitHub access
|
||||||
|
Get-GitHubRepository -OwnerName 'CriticalSolutionsNetwork' -RepositoryName 'M365FoundationsCISReport'
|
||||||
|
|
||||||
|
|
||||||
|
#########################################################################################
|
||||||
|
connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome
|
||||||
|
# Retrieve the subscribed SKUs
|
||||||
|
$sub = Get-MgSubscribedSku -All
|
||||||
|
|
||||||
|
# Define the product array
|
||||||
|
$ProductArray = @(
|
||||||
|
"Microsoft_Cloud_App_Security_App_Governance_Add_On",
|
||||||
|
"Defender_Threat_Intelligence",
|
||||||
|
"THREAT_INTELLIGENCE",
|
||||||
|
"WIN_DEF_ATP",
|
||||||
|
"Microsoft_Defender_for_Endpoint_F2",
|
||||||
|
"DEFENDER_ENDPOINT_P1",
|
||||||
|
"DEFENDER_ENDPOINT_P1_EDU",
|
||||||
|
"MDATP_XPLAT",
|
||||||
|
"MDATP_Server",
|
||||||
|
"ATP_ENTERPRISE_FACULTY",
|
||||||
|
"ATA",
|
||||||
|
"ATP_ENTERPRISE_GOV",
|
||||||
|
"ATP_ENTERPRISE_USGOV_GCCHIGH",
|
||||||
|
"THREAT_INTELLIGENCE_GOV",
|
||||||
|
"TVM_Premium_Standalone",
|
||||||
|
"TVM_Premium_Add_on",
|
||||||
|
"ATP_ENTERPRISE",
|
||||||
|
"Azure_Information_Protection_Premium_P1",
|
||||||
|
"Azure_Information_Protection_Premium_P2",
|
||||||
|
"Microsoft_Application_Protection_and_Governance",
|
||||||
|
"Exchange_Online_Protection",
|
||||||
|
"Microsoft_365_Defender",
|
||||||
|
"Cloud_App_Security_Discovery"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Define the hashtable
|
||||||
|
$ProductHashTable = @{
|
||||||
|
"App governance add-on to Microsoft Defender for Cloud Apps" = "Microsoft_Cloud_App_Security_App_Governance_Add_On"
|
||||||
|
"Defender Threat Intelligence" = "Defender_Threat_Intelligence"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 2)" = "THREAT_INTELLIGENCE"
|
||||||
|
"Microsoft Defender for Endpoint" = "WIN_DEF_ATP"
|
||||||
|
"Microsoft Defender for Endpoint F2" = "Microsoft_Defender_for_Endpoint_F2"
|
||||||
|
"Microsoft Defender for Endpoint P1" = "DEFENDER_ENDPOINT_P1"
|
||||||
|
"Microsoft Defender for Endpoint P1 for EDU" = "DEFENDER_ENDPOINT_P1_EDU"
|
||||||
|
"Microsoft Defender for Endpoint P2_XPLAT" = "MDATP_XPLAT"
|
||||||
|
"Microsoft Defender for Endpoint Server" = "MDATP_Server"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1) Faculty" = "ATP_ENTERPRISE_FACULTY"
|
||||||
|
"Microsoft Defender for Identity" = "ATA"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1) GCC" = "ATP_ENTERPRISE_GOV"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1)_USGOV_GCCHIGH" = "ATP_ENTERPRISE_USGOV_GCCHIGH"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 2) GCC" = "THREAT_INTELLIGENCE_GOV"
|
||||||
|
"Microsoft Defender Vulnerability Management" = "TVM_Premium_Standalone"
|
||||||
|
"Microsoft Defender Vulnerability Management Add-on" = "TVM_Premium_Add_on"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1)" = "ATP_ENTERPRISE"
|
||||||
|
"Azure Information Protection Premium P1" = "Azure_Information_Protection_Premium_P1"
|
||||||
|
"Azure Information Protection Premium P2" = "Azure_Information_Protection_Premium_P2"
|
||||||
|
"Microsoft Application Protection and Governance" = "Microsoft_Application_Protection_and_Governance"
|
||||||
|
"Exchange Online Protection" = "Exchange_Online_Protection"
|
||||||
|
"Microsoft 365 Defender" = "Microsoft_365_Defender"
|
||||||
|
"Cloud App Security Discovery" = "Cloud_App_Security_Discovery"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Reverse the hashtable
|
||||||
|
$ReverseProductHashTable = @{}
|
||||||
|
foreach ($key in $ProductHashTable.Keys) {
|
||||||
|
$ReverseProductHashTable[$ProductHashTable[$key]] = $key
|
||||||
|
}
|
||||||
|
|
||||||
|
# Loop through each SKU and get the enabled security features
|
||||||
|
$securityFeatures = foreach ($sku in $sub) {
|
||||||
|
if ($sku.SkuPartNumber -eq "MDATP_XPLAT_EDU") {
|
||||||
|
Write-Host "the SKU is: `n$($sku | gm)"
|
||||||
|
[PSCustomObject]@{
|
||||||
|
Skupartnumber = $sku.skupartnumber
|
||||||
|
AppliesTo = $sku.AppliesTo
|
||||||
|
ProvisioningStatus = $sku.ProvisioningStatus
|
||||||
|
ServicePlanId = $sku.ServicePlanId
|
||||||
|
ServicePlanName = $sku.ServicePlanName
|
||||||
|
FriendlyName = "Defender P2 for EDU"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
$sku.serviceplans | Where-Object { $_.serviceplanname -in $ProductArray } | ForEach-Object {
|
||||||
|
$friendlyName = $ReverseProductHashTable[$_.ServicePlanName]
|
||||||
|
[PSCustomObject]@{
|
||||||
|
Skupartnumber = $sku.skupartnumber
|
||||||
|
AppliesTo = $_.AppliesTo
|
||||||
|
ProvisioningStatus = $_.ProvisioningStatus
|
||||||
|
ServicePlanId = $_.ServicePlanId
|
||||||
|
ServicePlanName = $_.ServicePlanName
|
||||||
|
FriendlyName = $friendlyName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# Output the security features
|
||||||
|
$securityFeatures | Format-Table -AutoSize
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##########
|
||||||
|
|
||||||
|
# Ensure the ImportExcel module is available
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure the ImportExcel module is available
|
||||||
|
if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
|
||||||
|
Install-Module -Name ImportExcel -Force -Scope CurrentUser
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to wait until the file is available
|
||||||
|
function Wait-ForFile {
|
||||||
|
param (
|
||||||
|
[string]$FilePath
|
||||||
|
)
|
||||||
|
while (Test-Path -Path $FilePath -PathType Leaf -and -not (Get-Content $FilePath -ErrorAction SilentlyContinue)) {
|
||||||
|
Start-Sleep -Seconds 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Path to the Excel file
|
||||||
|
$excelFilePath = "C:\Users\dougrios\OneDrive - CRITICALSOLUTIONS NET LLC\Documents\_Tools\Benchies\SKUs.xlsx"
|
||||||
|
|
||||||
|
# Wait for the file to be available
|
||||||
|
|
||||||
|
|
||||||
|
# Import the Excel file
|
||||||
|
$excelData = Import-Excel -Path $excelFilePath
|
||||||
|
|
||||||
|
# Retrieve the subscribed SKUs
|
||||||
|
$subscribedSkus = Get-MgSubscribedSku -All
|
||||||
|
|
||||||
|
# Define the hashtable with security-related product names
|
||||||
|
$ProductHashTable = @{
|
||||||
|
"App governance add-on to Microsoft Defender for Cloud Apps" = "Microsoft_Cloud_App_Security_App_Governance_Add_On"
|
||||||
|
"Defender Threat Intelligence" = "Defender_Threat_Intelligence"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 2)" = "THREAT_INTELLIGENCE"
|
||||||
|
"Microsoft Defender for Endpoint" = "WIN_DEF_ATP"
|
||||||
|
"Microsoft Defender for Endpoint F2" = "Microsoft_Defender_for_Endpoint_F2"
|
||||||
|
"Microsoft Defender for Endpoint P1" = "DEFENDER_ENDPOINT_P1"
|
||||||
|
"Microsoft Defender for Endpoint P1 for EDU" = "DEFENDER_ENDPOINT_P1_EDU"
|
||||||
|
"Microsoft Defender for Endpoint P2_XPLAT" = "MDATP_XPLAT"
|
||||||
|
"Microsoft Defender for Endpoint Server" = "MDATP_Server"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1) Faculty" = "ATP_ENTERPRISE_FACULTY"
|
||||||
|
"Microsoft Defender for Identity" = "ATA"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1) GCC" = "ATP_ENTERPRISE_GOV"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1)_USGOV_GCCHIGH" = "ATP_ENTERPRISE_USGOV_GCCHIGH"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 2) GCC" = "THREAT_INTELLIGENCE_GOV"
|
||||||
|
"Microsoft Defender Vulnerability Management" = "TVM_Premium_Standalone"
|
||||||
|
"Microsoft Defender Vulnerability Management Add-on" = "TVM_Premium_Add_on"
|
||||||
|
"Microsoft Defender for Office 365 (Plan 1)" = "ATP_ENTERPRISE"
|
||||||
|
"Azure Information Protection Premium P1" = "Azure_Information_Protection_Premium_P1"
|
||||||
|
"Azure Information Protection Premium P2" = "Azure_Information_Protection_Premium_P2"
|
||||||
|
"Microsoft Application Protection and Governance" = "Microsoft_Application_Protection_and_Governance"
|
||||||
|
"Exchange Online Protection" = "Exchange_Online_Protection"
|
||||||
|
"Microsoft 365 Defender" = "Microsoft_365_Defender"
|
||||||
|
"Cloud App Security Discovery" = "Cloud_App_Security_Discovery"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create a hashtable to store the SKU part numbers and their associated security features
|
||||||
|
$skuSecurityFeatures = @{}
|
||||||
|
|
||||||
|
# Populate the hashtable with data from the Excel file
|
||||||
|
foreach ($row in $excelData) {
|
||||||
|
if ($null -ne $row.'String ID' -and $null -ne $row.'Service plans included (friendly names)') {
|
||||||
|
$skuSecurityFeatures[$row.'String ID'] = $row.'Service plans included (friendly names)'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display the SKU part numbers and their associated security features
|
||||||
|
foreach ($sku in $subscribedSkus) {
|
||||||
|
$skuPartNumber = $sku.SkuPartNumber
|
||||||
|
if ($skuSecurityFeatures.ContainsKey($skuPartNumber)) {
|
||||||
|
$securityFeatures = $skuSecurityFeatures[$skuPartNumber]
|
||||||
|
|
||||||
|
# Check if the security feature is in the hashtable
|
||||||
|
$isSecurityFeature = $ProductHashTable.ContainsKey($securityFeatures)
|
||||||
|
|
||||||
|
if ($isSecurityFeature) {
|
||||||
|
Write-Output "SKU Part Number: $skuPartNumber"
|
||||||
|
Write-Output "Security Features: $securityFeatures (Security-related)"
|
||||||
|
} else {
|
||||||
|
Write-Output "SKU Part Number: $skuPartNumber"
|
||||||
|
Write-Output "Security Features: $securityFeatures"
|
||||||
|
}
|
||||||
|
Write-Output "----------------------------"
|
||||||
|
} else {
|
||||||
|
Write-Output "SKU Part Number: $skuPartNumber"
|
||||||
|
Write-Output "Security Features: Not Found in Excel"
|
||||||
|
Write-Output "----------------------------"
|
||||||
|
}
|
||||||
|
}
|
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
27
tests/Unit/Private/Format-RequiredModuleList.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-MostCommonWord.tests.ps1
Normal file
27
tests/Unit/Private/Get-MostCommonWord.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-RequiredModule.tests.ps1
Normal file
27
tests/Unit/Private/Get-RequiredModule.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-TestDefinitionsObject.tests.ps1
Normal file
27
tests/Unit/Private/Get-TestDefinitionsObject.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
27
tests/Unit/Private/Get-UniqueConnection.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Invoke-TestFunction.tests.ps1
Normal file
27
tests/Unit/Private/Invoke-TestFunction.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Measure-AuditResult.tests.ps1
Normal file
27
tests/Unit/Private/Measure-AuditResult.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/New-MergedObject.tests.ps1
Normal file
27
tests/Unit/Private/New-MergedObject.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
27
tests/Unit/Private/Test-IsAdmin.tests.ps1
Normal file
27
tests/Unit/Private/Test-IsAdmin.tests.ps1
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
$ProjectPath = "$PSScriptRoot\..\..\.." | Convert-Path
|
||||||
|
$ProjectName = ((Get-ChildItem -Path $ProjectPath\*\*.psd1).Where{
|
||||||
|
($_.Directory.Name -match 'source|src' -or $_.Directory.Name -eq $_.BaseName) -and
|
||||||
|
$(try { Test-ModuleManifest $_.FullName -ErrorAction Stop } catch { $false } )
|
||||||
|
}).BaseName
|
||||||
|
|
||||||
|
|
||||||
|
Import-Module $ProjectName
|
||||||
|
|
||||||
|
InModuleScope $ProjectName {
|
||||||
|
Describe Get-PrivateFunction {
|
||||||
|
Context 'Default' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-PrivateFunction -PrivateData 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a string based on the parameter PrivateData' {
|
||||||
|
$return | Should -Be 'string'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
71
tests/Unit/Public/Get-MFAStatus.tests.ps1
Normal file
71
tests/Unit/Public/Get-MFAStatus.tests.ps1
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
BeforeAll {
|
||||||
|
$script:moduleName = '<% $PLASTER_PARAM_ModuleName %>'
|
||||||
|
|
||||||
|
# If the module is not found, run the build task 'noop'.
|
||||||
|
if (-not (Get-Module -Name $script:moduleName -ListAvailable))
|
||||||
|
{
|
||||||
|
# Redirect all streams to $null, except the error stream (stream 2)
|
||||||
|
& "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 2>&1 4>&1 5>&1 6>&1 > $null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Re-import the module using force to get any code changes between runs.
|
||||||
|
Import-Module -Name $script:moduleName -Force -ErrorAction 'Stop'
|
||||||
|
|
||||||
|
$PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:moduleName
|
||||||
|
$PSDefaultParameterValues['Mock:ModuleName'] = $script:moduleName
|
||||||
|
$PSDefaultParameterValues['Should:ModuleName'] = $script:moduleName
|
||||||
|
}
|
||||||
|
|
||||||
|
AfterAll {
|
||||||
|
$PSDefaultParameterValues.Remove('Mock:ModuleName')
|
||||||
|
$PSDefaultParameterValues.Remove('InModuleScope:ModuleName')
|
||||||
|
$PSDefaultParameterValues.Remove('Should:ModuleName')
|
||||||
|
|
||||||
|
Remove-Module -Name $script:moduleName
|
||||||
|
}
|
||||||
|
|
||||||
|
Describe Get-Something {
|
||||||
|
|
||||||
|
Context 'Return values' {
|
||||||
|
BeforeEach {
|
||||||
|
$return = Get-Something -Data 'value'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Returns a single object' {
|
||||||
|
($return | Measure-Object).Count | Should -Be 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Context 'Pipeline' {
|
||||||
|
It 'Accepts values from the pipeline by value' {
|
||||||
|
$return = 'value1', 'value2' | Get-Something
|
||||||
|
|
||||||
|
$return[0] | Should -Be 'value1'
|
||||||
|
$return[1] | Should -Be 'value2'
|
||||||
|
}
|
||||||
|
|
||||||
|
It 'Accepts value 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 'ShouldProcess' {
|
||||||
|
It 'Supports WhatIf' {
|
||||||
|
(Get-Command Get-Something).Parameters.ContainsKey('WhatIf') | Should -Be $true
|
||||||
|
{ Get-Something -Data 'value' -WhatIf } | Should -Not -Throw
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in New Issue
Block a user