Merge pull request #111 from CriticalSolutionsNetwork/6.1.2/6.1.3-refactor
6.1.2/6.1.3 refactor 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"
This commit is contained in:
11
CHANGELOG.md
11
CHANGELOG.md
@@ -6,6 +6,17 @@ 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.
|
- Added condition comments to each test.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
BIN
docs/index.html
BIN
docs/index.html
Binary file not shown.
@@ -4,7 +4,7 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
|
|||||||
|
|
||||||
|
|
||||||
<#
|
<#
|
||||||
$ver = "v0.1.9"
|
$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 refactor Update"
|
git tag -a $ver -m "Release version $ver refactor Update"
|
||||||
|
@@ -18,7 +18,7 @@ function Connect-M365Suite {
|
|||||||
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
Write-Host "Successfully connected to Azure Active Directory." -ForegroundColor Green
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
|
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "EXO | Microsoft Graph") {
|
||||||
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
Write-Host "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
|
||||||
try {
|
try {
|
||||||
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
|
||||||
@@ -31,7 +31,7 @@ function Connect-M365Suite {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") {
|
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "EXO | Microsoft Graph") {
|
||||||
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
Write-Host "Connecting to Exchange Online..." -ForegroundColor Cyan
|
||||||
Connect-ExchangeOnline | Out-Null
|
Connect-ExchangeOnline | Out-Null
|
||||||
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
Write-Host "Successfully connected to Exchange Online." -ForegroundColor Green
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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,AzureAD | EXO | 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 | 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,AzureAD | 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 | 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
|
||||||
|
|
@@ -29,32 +29,36 @@ function Test-MailboxAuditingE3 {
|
|||||||
# 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"
|
$recnum = "6.1.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
if (($founde3Sku.count)-ne 0) {
|
||||||
foreach ($user in $allUsers) {
|
$allUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde3Sku.SkuId) )" -All
|
||||||
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||||
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
try {
|
||||||
continue
|
foreach ($user in $allUsers) {
|
||||||
}
|
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
||||||
|
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
#$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
||||||
$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
|
#$hasOfficeE3 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e3SkuPartNumbers }).Count -gt 0
|
||||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
|
#Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E3 license."
|
||||||
|
|
||||||
if ($hasOfficeE3) {
|
|
||||||
$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) {
|
||||||
@@ -84,39 +88,49 @@ function Test-MailboxAuditingE3 {
|
|||||||
# 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
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
||||||
$details = if ($allFailures.Count -eq 0) {
|
$details = if ($allFailures.Count -eq 0) {
|
||||||
"All Office E3 users have correct mailbox audit settings."
|
"All Office E3 users have correct mailbox audit settings."
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
|
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n")
|
||||||
}
|
}
|
||||||
|
|
||||||
# Populate the audit result
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = $recnum
|
Rec = $recnum
|
||||||
Result = $allFailures.Count -eq 0
|
Result = $false
|
||||||
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
Status = "Fail"
|
||||||
Details = $details
|
Details = "No M365 E3 licenses found."
|
||||||
FailureReason = $failureReasons
|
FailureReason = "The audit is for M365 E3 licenses, but no such licenses were found."
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$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 {
|
||||||
|
@@ -27,32 +27,35 @@ function Test-MailboxAuditingE5 {
|
|||||||
# - Condition C: AuditDelegate actions do not include all of the following: ApplyRecord, Create, HardDelete, MailItemsAccessed, MoveToDeletedItems, SendAs, SendOnBehalf, SoftDelete, Update, 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.
|
# - Condition D: AuditOwner actions do not include all of the following: ApplyRecord, HardDelete, MailItemsAccessed, MoveToDeletedItems, Send, SoftDelete, Update, UpdateCalendarDelegation, UpdateFolderPermissions, UpdateInboxRules.
|
||||||
|
|
||||||
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
|
$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"
|
$recnum = "6.1.3"
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
if (($founde5Sku.count) -ne 0) {
|
||||||
foreach ($user in $allUsers) {
|
$allUsers = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde5Sku.SkuId) )" -All
|
||||||
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
$mailboxes = Get-EXOMailbox -PropertySets Audit
|
||||||
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
try {
|
||||||
continue
|
foreach ($user in $allUsers) {
|
||||||
}
|
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
|
||||||
|
Write-Verbose "Skipping already processed user: $($user.UserPrincipalName)"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
#$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
|
||||||
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
#$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
|
||||||
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
#Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
|
||||||
|
$mailbox = $mailboxes | Where-Object { $_.UserPrincipalName -eq $user.UserPrincipalName }
|
||||||
if ($hasOfficeE5) {
|
|
||||||
$userUPN = $user.UserPrincipalName
|
$userUPN = $user.UserPrincipalName
|
||||||
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
#$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
|
||||||
|
|
||||||
$missingActions = @()
|
$missingActions = @()
|
||||||
if ($mailbox.AuditEnabled) {
|
if ($mailbox.AuditEnabled) {
|
||||||
@@ -81,39 +84,49 @@ function Test-MailboxAuditingE5 {
|
|||||||
# 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
|
# Prepare failure reasons and details based on compliance
|
||||||
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
$failureReasons = if ($allFailures.Count -eq 0) { "N/A" } else { "Audit issues detected." }
|
||||||
$details = if ($allFailures.Count -eq 0) {
|
$details = if ($allFailures.Count -eq 0) {
|
||||||
"All Office E5 users have correct mailbox audit settings." # Condition A for pass
|
"All Office E5 users have correct mailbox audit settings." # Condition A for pass
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n") # Condition A for fail
|
"UserPrincipalName|AuditEnabled|AdminActionsMissing|DelegateActionsMissing|OwnerActionsMissing`n" + ($allFailures -join "`n") # Condition A for fail
|
||||||
}
|
}
|
||||||
|
|
||||||
# Populate the audit result
|
# Populate the audit result
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $allFailures.Count -eq 0
|
||||||
|
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
|
# Retrieve the description from the test definitions
|
||||||
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = $recnum
|
Rec = $recnum
|
||||||
Result = $allFailures.Count -eq 0
|
Result = $false
|
||||||
Status = if ($allFailures.Count -eq 0) { "Pass" } else { "Fail" }
|
Status = "Fail"
|
||||||
Details = $details
|
Details = "No M365 E5 licenses found."
|
||||||
FailureReason = $failureReasons
|
FailureReason = "The audit is for M365 E5 licenses, but no such licenses were found."
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$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,10 +1,7 @@
|
|||||||
function Test-SafeAttachmentsPolicy {
|
function Test-SafeAttachmentsPolicy {
|
||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
[OutputType([CISAuditResult])]
|
[OutputType([CISAuditResult])]
|
||||||
param (
|
param ()
|
||||||
# Aligned
|
|
||||||
# Parameters can be added if needed
|
|
||||||
)
|
|
||||||
|
|
||||||
begin {
|
begin {
|
||||||
# Dot source the class script if necessary
|
# Dot source the class script if necessary
|
||||||
@@ -13,7 +10,7 @@ function Test-SafeAttachmentsPolicy {
|
|||||||
# Initialization code, if needed
|
# Initialization code, if needed
|
||||||
$recnum = "2.1.4"
|
$recnum = "2.1.4"
|
||||||
|
|
||||||
<#
|
<#
|
||||||
Conditions for 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
Conditions for 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
||||||
|
|
||||||
Validate test for a pass:
|
Validate test for a pass:
|
||||||
@@ -31,58 +28,86 @@ function Test-SafeAttachmentsPolicy {
|
|||||||
- Condition B: The policy does not cover all recipients within the organization.
|
- 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 C: The policy action is not set to "Dynamic Delivery" or "Quarantine".
|
||||||
- Condition D: The policy is disabled.
|
- Condition D: The policy is disabled.
|
||||||
#>
|
#>
|
||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) {
|
||||||
# 2.1.4 (L2) Ensure Safe Attachments policy is enabled
|
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 = @()
|
||||||
|
|
||||||
# Condition A: Check if any Safe Attachments policy is enabled
|
foreach ($policy in $safeAttachmentPolicies) {
|
||||||
$result = $null -ne $safeAttachmentPolicies -and $safeAttachmentPolicies.Count -gt 0
|
# Initialize policy detail and failed status
|
||||||
|
$failed = $false
|
||||||
|
|
||||||
# Condition B, C, D: Additional checks can be added here if more detailed policy attributes are required
|
# 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
|
||||||
|
}
|
||||||
|
|
||||||
# Determine details and failure reasons based on the presence of enabled policies
|
# Check if the policy is not disabled (Condition D)
|
||||||
$details = if ($result) {
|
if (-not $policy.Enable) {
|
||||||
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
|
$failureReasons += "Policy '$($policy.Name)' is disabled."
|
||||||
}
|
$failed = $true
|
||||||
else {
|
}
|
||||||
"No Safe Attachments Policies are enabled."
|
|
||||||
|
# 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: $_"
|
||||||
|
|
||||||
$failureReasons = if ($result) {
|
# Retrieve the description from the test definitions
|
||||||
"N/A"
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
}
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
else {
|
|
||||||
"Safe Attachments policy is not enabled."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = $recnum
|
Rec = $recnum
|
||||||
Result = $result
|
Result = $false
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
Status = "Fail"
|
||||||
Details = $details
|
Details = "No M365 E5 licenses found."
|
||||||
FailureReason = $failureReasons
|
FailureReason = "The audit is for M365 E5 licenses and the required EXO commands will not be available otherwise."
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$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 {
|
||||||
@@ -90,4 +115,3 @@ function Test-SafeAttachmentsPolicy {
|
|||||||
return $auditResult
|
return $auditResult
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,61 +31,71 @@ function Test-SafeAttachmentsTeams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) {
|
||||||
# 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled
|
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."
|
||||||
|
}
|
||||||
|
|
||||||
|
$failureReasons = if ($result) {
|
||||||
|
"N/A"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Condition A: Check Safe Attachments for SharePoint
|
# Retrieve the description from the test definitions
|
||||||
# Condition B: Check Safe Attachments for OneDrive
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
# Condition C: Check Safe Attachments for Microsoft Teams
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
|
|
||||||
# Determine the result based on the ATP policy settings
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
$result = $null -ne $atpPolicyResult
|
|
||||||
$details = if ($result) {
|
|
||||||
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
|
|
||||||
}
|
|
||||||
|
|
||||||
$failureReasons = if ($result) {
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
"N/A"
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
"ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured."
|
else {
|
||||||
}
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = $recnum
|
Rec = $recnum
|
||||||
Result = $result
|
Result = $false
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
Status = "Fail"
|
||||||
Details = $details
|
Details = "No M365 E5 licenses found."
|
||||||
FailureReason = $failureReasons
|
FailureReason = "The audit is for M365 E5 licenses and the required EXO commands will not be available otherwise."
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$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 {
|
||||||
|
@@ -40,62 +40,72 @@ function Test-SafeLinksOfficeApps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
process {
|
process {
|
||||||
try {
|
if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) {
|
||||||
# 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled
|
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
|
|
||||||
# Condition A: Checking policy settings
|
|
||||||
$failures = @()
|
|
||||||
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On
|
|
||||||
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On
|
|
||||||
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On
|
|
||||||
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On
|
|
||||||
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off
|
|
||||||
|
|
||||||
# Only add details for policies that have misconfigurations
|
|
||||||
if ($failures.Count -gt 0) {
|
|
||||||
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Prepare the final result
|
||||||
|
# Condition B: Ensuring no misconfigurations
|
||||||
|
$result = $misconfiguredDetails.Count -eq 0
|
||||||
|
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
||||||
|
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
||||||
|
|
||||||
|
# Create and populate the CISAuditResult object
|
||||||
|
$params = @{
|
||||||
|
Rec = $recnum
|
||||||
|
Result = $result
|
||||||
|
Status = if ($result) { "Pass" } else { "Fail" }
|
||||||
|
Details = $details
|
||||||
|
FailureReason = $failureReasons
|
||||||
|
}
|
||||||
|
$auditResult = Initialize-CISAuditResult @params
|
||||||
}
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "An error occurred during the test: $_"
|
||||||
|
|
||||||
# Prepare the final result
|
# Retrieve the description from the test definitions
|
||||||
# Condition B: Ensuring no misconfigurations
|
$testDefinition = $script:TestDefinitionsObject | Where-Object { $_.Rec -eq $recnum }
|
||||||
$result = $misconfiguredDetails.Count -eq 0
|
$description = if ($testDefinition) { $testDefinition.RecDescription } else { "Description not found" }
|
||||||
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
|
|
||||||
$failureReasons = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
|
|
||||||
|
|
||||||
# Create and populate the CISAuditResult object
|
$script:FailedTests.Add([PSCustomObject]@{ Rec = $recnum; Description = $description; Error = $_ })
|
||||||
|
|
||||||
|
# Call Initialize-CISAuditResult with error parameters
|
||||||
|
$auditResult = Initialize-CISAuditResult -Rec $recnum -Failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
$params = @{
|
$params = @{
|
||||||
Rec = $recnum
|
Rec = $recnum
|
||||||
Result = $result
|
Result = $false
|
||||||
Status = if ($result) { "Pass" } else { "Fail" }
|
Status = "Fail"
|
||||||
Details = $details
|
Details = "No M365 E5 licenses found."
|
||||||
FailureReason = $failureReasons
|
FailureReason = "The audit is for M365 E5 licenses and the required EXO commands will not be available otherwise."
|
||||||
}
|
}
|
||||||
$auditResult = Initialize-CISAuditResult @params
|
$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 {
|
||||||
|
193
test-gh.ps1
193
test-gh.ps1
@@ -210,3 +210,196 @@ if ($warnings.Count -gt 0) {
|
|||||||
Get-GitHubRepository -OwnerName 'CriticalSolutionsNetwork' -RepositoryName 'M365FoundationsCISReport'
|
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 "----------------------------"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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