Initial Commit

This commit is contained in:
DrIOS
2024-03-25 08:34:43 -05:00
commit 0226b293b5
137 changed files with 9448 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
class CISAuditResult {
[string]$Status
[string]$ELevel
[string]$ProfileLevel
[string]$Rec
[string]$RecDescription
[string]$CISControlVer = 'v8'
[string]$CISControl
[string]$CISDescription
[bool]$IG1
[bool]$IG2
[bool]$IG3
[bool]$Result
[string]$Details
[string]$FailureReason
}

View File

@@ -0,0 +1,136 @@
#
# Module manifest for module 'M365FoundationsCISReport'
#
# Generated by: Douglas S. Rios (DrIOSx)
#
# Generated on: 3/25/2024
#
@{
# Script module or binary module file associated with this manifest.
RootModule = 'M365FoundationsCISReport.psm1'
# Version number of this module.
ModuleVersion = '0.0.1'
# Supported PSEditions
# CompatiblePSEditions = @()
# ID used to uniquely identify this module
GUID = '0d064bfb-d1ce-484b-a173-993b55984dc9'
# Author of this module
Author = 'Douglas S. Rios (DrIOSx)'
# Company or vendor of this module
CompanyName = 'CrticalSolutions Net LLC'
# Copyright statement for this module
Copyright = '(c) 2024 Douglas S. Rios (DrIOSx). All rights reserved.'
# Description of the functionality provided by this module
Description = 'Automated assessment of 50 CIS 365 Foundations v3.0.0 benchmark.'
# Minimum version of the Windows PowerShell engine required by this module
PowerShellVersion = '5.0'
# Name of the Windows PowerShell host required by this module
# PowerShellHostName = ''
# Minimum version of the Windows PowerShell host required by this module
# PowerShellHostVersion = ''
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# DotNetFrameworkVersion = ''
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# CLRVersion = ''
# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''
# Modules that must be imported into the global environment prior to importing this module
RequiredModules = @()
# Assemblies that must be loaded prior to importing this module
# RequiredAssemblies = @()
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()
# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()
# Format files (.ps1xml) to be loaded when importing this module
# FormatsToProcess = @()
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
# NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @()
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
# Variables to export from this module
VariablesToExport = @()
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()
# DSC resources to export from this module
DscResourcesToExport = @()
# List of all modules packaged with this module
# ModuleList = @()
# List of all files packaged with this module
# FileList = @()
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
Prerelease = ''
# Tags applied to this module. These help with module discovery in online galleries.
# Tags = @()
# A URL to the license for this module.
LicenseUri = 'https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en'
# A URL to the main website for this project.
# ProjectUri = ''
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
ReleaseNotes = ''
} # End of PSData hashtable
} # End of PrivateData hashtable
# HelpInfo URI of this module
# HelpInfoURI = ''
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''
}

View File

@@ -0,0 +1,5 @@
<#
This file is intentionally left empty. It is must be left here for the module
manifest to refer to. It is recreated during the build process.
#>

View File

@@ -0,0 +1,33 @@
function Assert-ModuleAvailability {
param(
[string]$ModuleName,
[string]$RequiredVersion,
[string]$SubModuleName
)
try {
$module = Get-Module -ListAvailable -Name $ModuleName | Where-Object { $_.Version -ge [version]$RequiredVersion }
if ($null -eq $module) {$auditResult.Profile
Write-Host "Installing $ModuleName module..."
Install-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force -AllowClobber -Scope CurrentUser | Out-Null
}
elseif ($module.Version -lt [version]$RequiredVersion) {
Write-Host "Updating $ModuleName module to required version..."
Update-Module -Name $ModuleName -RequiredVersion $RequiredVersion -Force | Out-Null
}
else {
Write-Host "$ModuleName module is already at required version or newer."
}
if ($SubModuleName) {
Import-Module -Name "$ModuleName.$SubModuleName" -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
}
else {
Import-Module -Name $ModuleName -RequiredVersion $RequiredVersion -ErrorAction Stop | Out-Null
}
}
catch {
Write-Warning "An error occurred with module $ModuleName`: $_"
}
}

View File

@@ -0,0 +1,56 @@
function Connect-M365Suite {
[CmdletBinding()]
param (
# Parameter to specify the SharePoint Online Tenant Admin URL
[Parameter(Mandatory)]
[string]$TenantAdminUrl
)
$VerbosePreference = "SilentlyContinue"
try {
# Attempt to connect to Azure Active Directory
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
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
}
catch {
Write-Host "Failed to connect o MgGraph, attempting device auth." -ForegroundColor Yellow
# Attempt to connect to Microsoft Graph with specified scopes
Write-Host "Connecting to Microsoft Graph using device auth with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" -ForegroundColor Cyan
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null
Write-Host "Successfully connected to Microsoft Graph with specified scopes." -ForegroundColor Green
}
# Validate SharePoint Online Tenant Admin URL
if (-not $TenantAdminUrl) {
throw "SharePoint Online Tenant Admin URL is required."
}
# Attempt to connect to SharePoint Online
Write-Host "Connecting to SharePoint Online..." -ForegroundColor Cyan
Connect-SPOService -Url $TenantAdminUrl | Out-Null
Write-Host "Successfully connected to SharePoint Online." -ForegroundColor Green
# Attempt to connect to Microsoft Teams
Write-Host "Connecting to Microsoft Teams..." -ForegroundColor Cyan
Connect-MicrosoftTeams | Out-Null
Write-Host "Successfully connected to Microsoft Teams." -ForegroundColor Green
}
catch {
$VerbosePreference = "Continue"
Write-Host "There was an error establishing one or more connections: $_" -ForegroundColor Red
throw $_
}
$VerbosePreference = "Continue"
}

View File

@@ -0,0 +1,39 @@
function Disconnect-M365Suite {
# Clean up sessions
try {
Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Green
Disconnect-ExchangeOnline -Confirm:$false | Out-Null
}
catch {
Write-Warning "Failed to disconnect from Exchange Online: $_"
}
try {
Write-Host "Disconnecting from Azure AD..." -ForegroundColor Green
Disconnect-AzureAD | Out-Null
}
catch {
Write-Warning "Failed to disconnect from Azure AD: $_"
}
try {
Write-Host "Disconnecting from Microsoft Graph..." -ForegroundColor Green
Disconnect-MgGraph | Out-Null
}
catch {
Write-Warning "Failed to disconnect from Microsoft Graph: $_"
}
try {
Write-Host "Disconnecting from SharePoint Online..." -ForegroundColor Green
Disconnect-SPOService | Out-Null
}
catch {
Write-Warning "Failed to disconnect from SharePoint Online: $_"
}
try {
Write-Host "Disconnecting from Microsoft Teams..." -ForegroundColor Green
Disconnect-MicrosoftTeams | Out-Null
}
catch {
Write-Warning "Failed to disconnect from Microsoft Teams: $_"
}
Write-Host "All sessions have been disconnected." -ForegroundColor Green
}

View File

@@ -0,0 +1,258 @@
<#
.SYNOPSIS
Invokes a security audit for Microsoft 365 environments.
.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.
.PARAMETER TenantAdminUrl
The URL of the tenant admin. This parameter is mandatory.
.PARAMETER DomainName
The domain name of the Microsoft 365 environment. This parameter is mandatory.
.PARAMETER ELevel
Specifies the E-Level (E3 or E5) for the audit. This parameter is optional and can be combined with the ProfileLevel parameter.
.PARAMETER ProfileLevel
Specifies the profile level (L1 or L2) for the audit. This parameter is optional and can be combined with the ELevel parameter.
.PARAMETER IncludeIG1
If specified, includes tests where IG1 is true.
.PARAMETER IncludeIG2
If specified, includes tests where IG2 is true.
.PARAMETER IncludeIG3
If specified, includes tests where IG3 is true.
.PARAMETER IncludeRecommendation
Specifies specific recommendations to include in the audit. Accepts an array of recommendation numbers.
.PARAMETER SkipRecommendation
Specifies specific recommendations to exclude from the audit. Accepts an array of recommendation numbers.
.PARAMETER DoNotConnect
If specified, the cmdlet will not establish a connection to Microsoft 365 services.
.PARAMETER DoNotDisconnect
If specified, the cmdlet will not disconnect from Microsoft 365 services after execution.
.PARAMETER NoModuleCheck
If specified, the cmdlet will not check for the presence of required modules.
.EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -ELevel "E5" -ProfileLevel "L1"
Performs a security audit for the E5 level and L1 profile in the specified Microsoft 365 environment.
.EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -IncludeIG1
Performs an audit including all tests where IG1 is true.
.EXAMPLE
PS> Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com" -SkipRecommendation '1.1.3', '2.1.1'
Performs an audit while excluding specific recommendations 1.1.3 and 2.1.1.
.EXAMPLE
PS> $auditResults = Invoke-M365SecurityAudit -TenantAdminUrl "https://contoso-admin.sharepoint.com" -DomainName "contoso.com"
PS> $auditResults | Export-Csv -Path "auditResults.csv" -NoTypeInformation
Captures the audit results into a variable and exports them to a CSV file.
.INPUTS
None. You cannot pipe objects to Invoke-M365SecurityAudit.
.OUTPUTS
CISAuditResult[]
The cmdlet returns an array of CISAuditResult objects representing the results of the security audit.
.NOTES
- This module is based on CIS benchmarks.
- Governed by the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.
- Commercial use is not permitted. This module cannot be sold or used for commercial purposes.
- Modifications and sharing are allowed under the same license.
- For full license details, visit: https://creativecommons.org/licenses/by-nc-sa/4.0/deed.en
.LINK
Online Version: [GitHub Repository URL]
#>
function Invoke-M365SecurityAudit {
[CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = 'Default')]
[OutputType([CISAuditResult[]])]
param (
[Parameter(Mandatory = $true)]
[string]$TenantAdminUrl,
[Parameter(Mandatory = $true)]
[string]$DomainName,
# E-Level with optional ProfileLevel selection
[Parameter(ParameterSetName = 'ELevelFilter')]
[ValidateSet('E3', 'E5')]
[string]$ELevel,
[Parameter(ParameterSetName = 'ELevelFilter')]
[ValidateSet('L1', 'L2')]
[string]$ProfileLevel,
# IG Filters, one at a time
[Parameter(ParameterSetName = 'IG1Filter')]
[switch]$IncludeIG1,
[Parameter(ParameterSetName = 'IG2Filter')]
[switch]$IncludeIG2,
[Parameter(ParameterSetName = 'IG3Filter')]
[switch]$IncludeIG3,
# Inclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'RecFilter')]
[ValidateSet(
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
'8.5.7', '8.6.1'
)]
[string[]]$IncludeRecommendation,
# Exclusion of specific recommendation numbers
[Parameter(ParameterSetName = 'SkipRecFilter')]
[ValidateSet(
'1.1.3', '1.2.1', '1.2.2', '1.3.1', '1.3.3', '1.3.6', '2.1.1', '2.1.2', `
'2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.1.7', '2.1.9', '3.1.1', '5.1.2.3', `
'5.1.8.1', '6.1.1', '6.1.2', '6.1.3', '6.2.1', '6.2.2', '6.2.3', '6.3.1', `
'6.5.1', '6.5.2', '6.5.3', '7.2.1', '7.2.10', '7.2.2', '7.2.3', '7.2.4', `
'7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.3.1', '7.3.2', '7.3.4', '8.1.1', `
'8.1.2', '8.2.1', '8.5.1', '8.5.2', '8.5.3', '8.5.4', '8.5.5', '8.5.6', `
'8.5.7', '8.6.1'
)]
[string[]]$SkipRecommendation,
# Common parameters for all parameter sets
[switch]$DoNotConnect,
[switch]$DoNotDisconnect,
[switch]$NoModuleCheck
)
Begin {
if ($script:MaximumFunctionCount -lt 8192) {
$script:MaximumFunctionCount = 8192
}
# Ensure required modules are installed
# Define the required modules and versions in a hashtable
if (!($NoModuleCheck)) {
$requiredModules = @(
@{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0" },
@{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Authentication" },
@{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModuleName = "Users" },
@{ 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) {
Assert-ModuleAvailability -ModuleName $module.ModuleName -RequiredVersion $module.RequiredVersion -SubModuleName $module.SubModuleName
}
}
# Loop through each required module and assert its availability
# Establishing connections
#if (!($DoNotConnect -or $DoNotTest)) {
# Establishing connections
if (!($DoNotConnect)) {
Connect-M365Suite -TenantAdminUrl $TenantAdminUrl
}
# Load test definitions from CSV
$testDefinitionsPath = Join-Path -Path $PSScriptRoot -ChildPath "helper\TestDefinitions.csv"
$testDefinitions = Import-Csv -Path $testDefinitionsPath
# Apply filters based on parameter sets
switch ($PSCmdlet.ParameterSetName) {
'ELevelFilter' {
if ($null -ne $ELevel -and $null -ne $ProfileLevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ELevel -eq $ELevel -and $_.ProfileLevel -eq $ProfileLevel
}
}
elseif ($null -ne $ELevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ELevel -eq $ELevel
}
}
elseif ($null -ne $ProfileLevel) {
$testDefinitions = $testDefinitions | Where-Object {
$_.ProfileLevel -eq $ProfileLevel
}
}
}
'IG1Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG1 -eq 'TRUE' }
}
'IG2Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG2 -eq 'TRUE' }
}
'IG3Filter' {
$testDefinitions = $testDefinitions | Where-Object { $_.IG3 -eq 'TRUE' }
}
'RecFilter' {
$testDefinitions = $testDefinitions | Where-Object { $IncludeRecommendation -contains $_.Rec }
}
'SkipRecFilter' {
$testDefinitions = $testDefinitions | Where-Object { $SkipRecommendation -notcontains $_.Rec }
}
}
# End switch ($PSCmdlet.ParameterSetName)
# Determine which test files to load based on filtering
$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:"
$testsToLoad | ForEach-Object { Write-Verbose " $_" }
} # End Begin
Process {
$allAuditResults = @() # Initialize a collection to hold all results
# Dynamically dot-source the test scripts
$testsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "tests"
$testFiles = Get-ChildItem -Path $testsFolderPath -Filter "Test-*.ps1" |
Where-Object { $testsToLoad -contains $_.BaseName }
# Import the test functions
$testFiles | ForEach-Object {
Try {
. $_.FullName
}
Catch {
Write-Error "Failed to load test function $($_.Name): $_"
}
}
# Execute each test function from the prepared list
foreach ($testFunction in $testFiles) {
$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")) {
Write-Host "Running $functionName..."
$result = & $functionName @paramList
# Assuming each function returns an array of CISAuditResult or a single CISAuditResult
$allAuditResults += $result
}
}
}
End {
# Return all collected audit results
return $allAuditResults
# Check if the Disconnect switch is present
if (!($DoNotDisconnect)) {
# Clean up sessions
Disconnect-M365Suite
}
}
}

View File

@@ -0,0 +1,24 @@
TOPIC
about_M365FoundationsCISReport
SHORT DESCRIPTION
Automated assessment of 50 CIS 365 Foundations v3.0.0 benchmark.
LONG DESCRIPTION
Automated assessment of 50 CIS 365 Foundations v3.0.0 benchmark.
EXAMPLES
PS C:\> {{ add examples here }}
NOTE:
Thank you to all those who contributed to this module, by writing code, sharing opinions, and provided feedback.
TROUBLESHOOTING NOTE:
Look out on the Github repository for issues and new releases.
SEE ALSO
- {{ Please add Project URI such as github }}}
KEYWORDS
{{ Add comma separated keywords here }}

View File

@@ -0,0 +1,51 @@
Index,TestFileName,Rec,ELevel,ProfileLevel,IG1,IG2,IG3
1,Test-AntiPhishingPolicy.ps1,2.1.7,E5,L1,FALSE,FALSE,TRUE
2,Test-AuditDisabledFalse.ps1,6.1.1,E3,L1,TRUE,TRUE,TRUE
3,Test-AuditLogSearch.ps1,3.1.1,E3,L1,TRUE,TRUE,TRUE
4,Test-BlockChannelEmails.ps1,8.1.2,E3,L1,FALSE,FALSE,FALSE
5,Test-BlockMailForwarding.ps1,6.2.1,E3,L1,FALSE,FALSE,FALSE
6,Test-BlockSharedMailboxSignIn.ps1,1.2.2,E3,L1,FALSE,FALSE,FALSE
7,Test-CommonAttachmentFilter.ps1,2.1.2,E3,L1,FALSE,TRUE,TRUE
8,Test-CustomerLockbox.ps1,1.3.6,E5,L2,FALSE,FALSE,FALSE
9,Test-DialInBypassLobby.ps1,8.5.4,E3,L1,FALSE,FALSE,FALSE
10,Test-DisallowInfectedFilesDownload.ps1,7.3.1,E5,L2,TRUE,TRUE,TRUE
11,Test-EnableDKIM.ps1,2.1.9,E3,L1,FALSE,TRUE,TRUE
12,Test-ExternalNoControl.ps1,8.5.7,E3,L1,FALSE,FALSE,FALSE
13,Test-ExternalSharingCalendars.ps1,1.3.3,E3,L2,FALSE,TRUE,TRUE
14,Test-GlobalAdminsCount.ps1,1.1.3,E3,L1,TRUE,TRUE,TRUE
15,Test-GuestAccessExpiration.ps1,7.2.9,E3,L1,FALSE,FALSE,FALSE
16,Test-IdentifyExternalEmail.ps1,6.2.3,E3,L1,FALSE,FALSE,FALSE
17,Test-LinkSharingRestrictions.ps1,7.2.7,E3,L1,TRUE,TRUE,TRUE
18,Test-MailboxAuditingE3.ps1,6.1.2,E3,L1,TRUE,TRUE,TRUE
19,Test-MailboxAuditingE5.ps1,6.1.3,E5,L1,TRUE,TRUE,TRUE
20,Test-MailTipsEnabled.ps1,6.5.2,E3,L2,FALSE,FALSE,FALSE
21,Test-ManagedApprovedPublicGroups.ps1,1.2.1,E3,L2,TRUE,TRUE,TRUE
22,Test-MeetingChatNoAnonymous.ps1,8.5.5,E3,L1,FALSE,FALSE,FALSE
23,Test-ModernAuthExchangeOnline.ps1,6.5.1,E3,L1,FALSE,TRUE,TRUE
24,Test-ModernAuthSharePoint.ps1,7.2.1,E3,L1,FALSE,TRUE,TRUE
25,Test-NoAnonymousMeetingJoin.ps1,8.5.1,E3,L2,FALSE,FALSE,FALSE
26,Test-NoAnonymousMeetingStart.ps1,8.5.2,E3,L1,FALSE,FALSE,FALSE
27,Test-NotifyMalwareInternal.ps1,2.1.3,E3,L1,FALSE,TRUE,TRUE
28,Test-NoWhitelistDomains.ps1,6.2.2,E3,L1,FALSE,FALSE,FALSE
29,Test-OneDriveContentRestrictions.ps1,7.2.4,E3,L2,TRUE,TRUE,TRUE
30,Test-OneDriveSyncRestrictions.ps1,7.3.2,E3,L2,FALSE,FALSE,FALSE
31,Test-OrganizersPresent.ps1,8.5.6,E3,L1,FALSE,FALSE,FALSE
32,Test-OrgOnlyBypassLobby.ps1,8.5.3,E3,L1,FALSE,FALSE,TRUE
33,Test-PasswordHashSync.ps1,5.1.8.1,E3,L1,FALSE,TRUE,TRUE
34,Test-PasswordNeverExpirePolicy.ps1,1.3.1,E3,L1,TRUE,TRUE,TRUE
35,Test-ReauthWithCode.ps1,7.2.10,E3,L1,FALSE,FALSE,FALSE
36,Test-ReportSecurityInTeams.ps1,8.6.1,E3,L1,FALSE,FALSE,FALSE
37,Test-RestrictCustomScripts.ps1,7.3.4,E3,L1,FALSE,FALSE,TRUE
38,Test-RestrictExternalSharing.ps1,7.2.3,E3,L1,TRUE,TRUE,TRUE
39,Test-RestrictOutlookAddins.ps1,6.3.1,E3,L2,FALSE,TRUE,TRUE
40,Test-RestrictStorageProvidersOutlook.ps1,6.5.3,E3,L2,TRUE,TRUE,TRUE
41,Test-RestrictTenantCreation.ps1,5.1.2.3,E3,L1,FALSE,FALSE,FALSE
42,Test-SafeAttachmentsPolicy.ps1,2.1.4,E5,L2,FALSE,FALSE,TRUE
43,Test-SafeAttachmentsTeams.ps1,2.1.5,E5,L2,TRUE,TRUE,TRUE
44,Test-SafeLinksOfficeApps.ps1,2.1.1,E5,L2,TRUE,TRUE,TRUE
45,Test-SharePointAADB2B.ps1,7.2.2,E3,L1,FALSE,FALSE,FALSE
46,Test-SharePointExternalSharingDomains.ps1,7.2.6,E3,L2,TRUE,TRUE,TRUE
47,Test-SharePointGuestsItemSharing.ps1,7.2.5,E3,L2,TRUE,TRUE,TRUE
48,Test-SpamPolicyAdminNotify.ps1,2.1.6,E3,L1,FALSE,TRUE,TRUE
49,Test-TeamsExternalAccess.ps1,8.2.1,E3,L2,FALSE,FALSE,FALSE
50,Test-TeamsExternalFileSharing.ps1,8.1.1,E3,L2,TRUE,TRUE,TRUE
1 Index TestFileName Rec ELevel ProfileLevel IG1 IG2 IG3
2 1 Test-AntiPhishingPolicy.ps1 2.1.7 E5 L1 FALSE FALSE TRUE
3 2 Test-AuditDisabledFalse.ps1 6.1.1 E3 L1 TRUE TRUE TRUE
4 3 Test-AuditLogSearch.ps1 3.1.1 E3 L1 TRUE TRUE TRUE
5 4 Test-BlockChannelEmails.ps1 8.1.2 E3 L1 FALSE FALSE FALSE
6 5 Test-BlockMailForwarding.ps1 6.2.1 E3 L1 FALSE FALSE FALSE
7 6 Test-BlockSharedMailboxSignIn.ps1 1.2.2 E3 L1 FALSE FALSE FALSE
8 7 Test-CommonAttachmentFilter.ps1 2.1.2 E3 L1 FALSE TRUE TRUE
9 8 Test-CustomerLockbox.ps1 1.3.6 E5 L2 FALSE FALSE FALSE
10 9 Test-DialInBypassLobby.ps1 8.5.4 E3 L1 FALSE FALSE FALSE
11 10 Test-DisallowInfectedFilesDownload.ps1 7.3.1 E5 L2 TRUE TRUE TRUE
12 11 Test-EnableDKIM.ps1 2.1.9 E3 L1 FALSE TRUE TRUE
13 12 Test-ExternalNoControl.ps1 8.5.7 E3 L1 FALSE FALSE FALSE
14 13 Test-ExternalSharingCalendars.ps1 1.3.3 E3 L2 FALSE TRUE TRUE
15 14 Test-GlobalAdminsCount.ps1 1.1.3 E3 L1 TRUE TRUE TRUE
16 15 Test-GuestAccessExpiration.ps1 7.2.9 E3 L1 FALSE FALSE FALSE
17 16 Test-IdentifyExternalEmail.ps1 6.2.3 E3 L1 FALSE FALSE FALSE
18 17 Test-LinkSharingRestrictions.ps1 7.2.7 E3 L1 TRUE TRUE TRUE
19 18 Test-MailboxAuditingE3.ps1 6.1.2 E3 L1 TRUE TRUE TRUE
20 19 Test-MailboxAuditingE5.ps1 6.1.3 E5 L1 TRUE TRUE TRUE
21 20 Test-MailTipsEnabled.ps1 6.5.2 E3 L2 FALSE FALSE FALSE
22 21 Test-ManagedApprovedPublicGroups.ps1 1.2.1 E3 L2 TRUE TRUE TRUE
23 22 Test-MeetingChatNoAnonymous.ps1 8.5.5 E3 L1 FALSE FALSE FALSE
24 23 Test-ModernAuthExchangeOnline.ps1 6.5.1 E3 L1 FALSE TRUE TRUE
25 24 Test-ModernAuthSharePoint.ps1 7.2.1 E3 L1 FALSE TRUE TRUE
26 25 Test-NoAnonymousMeetingJoin.ps1 8.5.1 E3 L2 FALSE FALSE FALSE
27 26 Test-NoAnonymousMeetingStart.ps1 8.5.2 E3 L1 FALSE FALSE FALSE
28 27 Test-NotifyMalwareInternal.ps1 2.1.3 E3 L1 FALSE TRUE TRUE
29 28 Test-NoWhitelistDomains.ps1 6.2.2 E3 L1 FALSE FALSE FALSE
30 29 Test-OneDriveContentRestrictions.ps1 7.2.4 E3 L2 TRUE TRUE TRUE
31 30 Test-OneDriveSyncRestrictions.ps1 7.3.2 E3 L2 FALSE FALSE FALSE
32 31 Test-OrganizersPresent.ps1 8.5.6 E3 L1 FALSE FALSE FALSE
33 32 Test-OrgOnlyBypassLobby.ps1 8.5.3 E3 L1 FALSE FALSE TRUE
34 33 Test-PasswordHashSync.ps1 5.1.8.1 E3 L1 FALSE TRUE TRUE
35 34 Test-PasswordNeverExpirePolicy.ps1 1.3.1 E3 L1 TRUE TRUE TRUE
36 35 Test-ReauthWithCode.ps1 7.2.10 E3 L1 FALSE FALSE FALSE
37 36 Test-ReportSecurityInTeams.ps1 8.6.1 E3 L1 FALSE FALSE FALSE
38 37 Test-RestrictCustomScripts.ps1 7.3.4 E3 L1 FALSE FALSE TRUE
39 38 Test-RestrictExternalSharing.ps1 7.2.3 E3 L1 TRUE TRUE TRUE
40 39 Test-RestrictOutlookAddins.ps1 6.3.1 E3 L2 FALSE TRUE TRUE
41 40 Test-RestrictStorageProvidersOutlook.ps1 6.5.3 E3 L2 TRUE TRUE TRUE
42 41 Test-RestrictTenantCreation.ps1 5.1.2.3 E3 L1 FALSE FALSE FALSE
43 42 Test-SafeAttachmentsPolicy.ps1 2.1.4 E5 L2 FALSE FALSE TRUE
44 43 Test-SafeAttachmentsTeams.ps1 2.1.5 E5 L2 TRUE TRUE TRUE
45 44 Test-SafeLinksOfficeApps.ps1 2.1.1 E5 L2 TRUE TRUE TRUE
46 45 Test-SharePointAADB2B.ps1 7.2.2 E3 L1 FALSE FALSE FALSE
47 46 Test-SharePointExternalSharingDomains.ps1 7.2.6 E3 L2 TRUE TRUE TRUE
48 47 Test-SharePointGuestsItemSharing.ps1 7.2.5 E3 L2 TRUE TRUE TRUE
49 48 Test-SpamPolicyAdminNotify.ps1 2.1.6 E3 L1 FALSE TRUE TRUE
50 49 Test-TeamsExternalAccess.ps1 8.2.1 E3 L2 FALSE FALSE FALSE
51 50 Test-TeamsExternalFileSharing.ps1 8.1.1 E3 L2 TRUE TRUE TRUE

View File

@@ -0,0 +1,59 @@
function Test-AntiPhishingPolicy {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# Retrieve and validate the anti-phishing policies
$antiPhishPolicies = Get-AntiPhishPolicy
$validatedPolicies = $antiPhishPolicies | Where-Object {
$_.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
$isCompliant = $validatedPolicies.Count -gt 0
# Prepare failure details if policies are not compliant
$failureDetails = if (-not $isCompliant) {
"No anti-phishing policy is fully compliant with CIS benchmark requirements."
} else {
"Compliant Anti-Phish Policy Names: " + ($validatedPolicies.Name -join ', ')
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.7"
$auditResult.RecDescription = "Ensure that an anti-phishing policy has been created"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.7"
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $true
$auditResult.Result = $isCompliant
$auditResult.Details = $failureDetails
$auditResult.FailureReason = if (-not $isCompliant) { "Anti-phishing policies do not meet CIS benchmark requirements." } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,43 @@
function Test-AuditDisabledFalse {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False'
# Pass if AuditDisabled is False. Fail otherwise.
$auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled
$auditNotDisabled = -not $auditDisabledConfig.AuditDisabled
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($auditNotDisabled) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.1.1"
$auditResult.RecDescription = "Ensure 'AuditDisabled' organizationally is set to 'False'"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect Audit Logs"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $auditNotDisabled
$auditResult.Details = if ($auditNotDisabled) { "Audit is not disabled organizationally" } else { "Audit is disabled organizationally" }
$auditResult.FailureReason = if (-not $auditNotDisabled) { "AuditDisabled is set to True" } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,43 @@
function Test-AuditLogSearch {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled
# Pass if UnifiedAuditLogIngestionEnabled is True. Fail otherwise.
$auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled
$auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($auditLogResult) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "3.1.1"
$auditResult.RecDescription = "Ensure Microsoft 365 audit log search is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect Audit Logs"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $auditLogResult
$auditResult.Details = "UnifiedAuditLogIngestionEnabled: $($auditLogConfig.UnifiedAuditLogIngestionEnabled)"
$auditResult.FailureReason = if (-not $auditLogResult) { "Audit log search is not enabled" } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,44 @@
function Test-BlockChannelEmails {
[CmdletBinding()]
param (
# Parameters can be added here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.1.2 (L1) Ensure users can't send emails to a channel email address
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global
$allowEmailIntoChannel = $teamsClientConfig.AllowEmailIntoChannel
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # This control is Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.1.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the benchmark
$auditResult.IG2 = $false # Set based on the benchmark
$auditResult.IG3 = $false # Set based on the benchmark
$auditResult.RecDescription = "Ensure users can't send emails to a channel email address"
$auditResult.Result = -not $allowEmailIntoChannel
$auditResult.Details = "AllowEmailIntoChannel is set to $allowEmailIntoChannel"
$auditResult.FailureReason = if ($allowEmailIntoChannel) { "Emails can be sent to a channel email address" } else { "N/A" }
$auditResult.Status = if (-not $allowEmailIntoChannel) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,51 @@
function Test-BlockMailForwarding {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$auditResult = [CISAuditResult]::new()
$auditResult.Rec = "6.2.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure all forms of mail forwarding are blocked and/or disabled"
}
process {
# Verify that no rules are forwarding the email to external domains
$transportRules = Get-TransportRule | Where-Object { $_.RedirectMessageTo -ne $null }
$forwardingBlocked = $transportRules.Count -eq 0
$auditResult.Result = $forwardingBlocked
$auditResult.Details = if ($transportRules.Count -gt 0) {
$transportRules | ForEach-Object {
"$($_.Name) redirects to $($_.RedirectMessageTo)"
} -join " | "
} else {
"Step 1: No forwarding rules found. Please proceed with Step 2 described in CIS Benchmark."
}
$auditResult.FailureReason = if (-not $forwardingBlocked) {
"Mail forwarding rules found: $($transportRules.Name -join ', ')"
} else {
"N/A"
}
$auditResult.Status = if ($forwardingBlocked) { "Pass" } else { "Fail" }
}
end {
# Return the result object
return $auditResult
}
}

View File

@@ -0,0 +1,47 @@
function Test-BlockSharedMailboxSignIn {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 1.2.2 (L1) Ensure sign-in to shared mailboxes is blocked
# Pass if all shared mailboxes have AccountEnabled set to False.
# Fail if any shared mailbox has AccountEnabled set to True.
$MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox
$sharedMailboxDetails = $MBX | ForEach-Object { Get-AzureADUser -ObjectId $_.ExternalDirectoryObjectId }
$enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName }
$allBlocked = $enabledMailboxes.Count -eq 0
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Control is explicitly not mapped
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "1.2.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Control is not mapped, hence IG1 is false
$auditResult.IG2 = $false # Control is not mapped, hence IG2 is false
$auditResult.IG3 = $false # Control is not mapped, hence IG3 is false
$auditResult.RecDescription = "Ensure sign-in to shared mailboxes is blocked"
$auditResult.Result = $allBlocked
$auditResult.Details = "Enabled Mailboxes: $($enabledMailboxes -join ', ')"
$auditResult.FailureReason = if ($allBlocked) { "N/A" } else { "Some mailboxes have sign-in enabled: $($enabledMailboxes -join ', ')" }
$auditResult.Status = if ($allBlocked) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,46 @@
function Test-CommonAttachmentFilter {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$auditResults = @()
}
process {
# 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled
# Pass if EnableFileFilter is set to True. Fail otherwise.
$attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter
$result = $attachmentFilter.EnableFileFilter
$details = "File Filter Enabled: $($attachmentFilter.EnableFileFilter)"
$failureReason = if ($result) { "N/A" } else { "Common Attachment Types Filter is disabled" }
$status = if ($result) { "Pass" } else { "Fail" }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = $status
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.2"
$auditResult.RecDescription = "Ensure the Common Attachment Types Filter is enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.6"
$auditResult.CISDescription = "Block Unnecessary File Types"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,40 @@
function Test-CustomerLockbox {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
$auditResults = @()
}
process {
# 1.3.6 (L2) Ensure the customer lockbox feature is enabled
$orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled
$customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($customerLockboxEnabled) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "1.3.6"
$auditResult.RecDescription = "Ensure the customer lockbox feature is enabled"
$auditResult.CISControlVer = 'v8'
$auditResult.CISControl = "0.0" # As per the snapshot provided, this is explicitly not mapped
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.Result = $customerLockboxEnabled
$auditResult.Details = "Customer Lockbox Enabled: $customerLockboxEnabled"
$auditResult.FailureReason = if ($customerLockboxEnabled) { "N/A" } else { "Customer lockbox feature is not enabled." }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,45 @@
function Test-DialInBypassLobby {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.4 (L1) Ensure users dialing in can't bypass the lobby
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby
$PSTNBypassDisabled = -not $CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.4"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure users dialing in can't bypass the lobby"
$auditResult.Result = $PSTNBypassDisabled
$auditResult.Details = "AllowPSTNUsersToBypassLobby is set to $($CsTeamsMeetingPolicyPSTN.AllowPSTNUsersToBypassLobby)"
$auditResult.FailureReason = if ($PSTNBypassDisabled) { "N/A" } else { "Users dialing in can bypass the lobby" }
$auditResult.Status = if ($PSTNBypassDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,41 @@
function Test-DisallowInfectedFilesDownload {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.3.1 (L2) Ensure Office 365 SharePoint infected files are disallowed for download
$SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload
$isDisallowInfectedFileDownloadEnabled = $SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "10.1"
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
$auditResult.Rec = "7.3.1"
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure Office 365 SharePoint infected files are disallowed for download"
$auditResult.Result = $isDisallowInfectedFileDownloadEnabled
$auditResult.Details = "DisallowInfectedFileDownload: $($SPOTenantDisallowInfectedFileDownload.DisallowInfectedFileDownload)"
$auditResult.FailureReason = if (-not $isDisallowInfectedFileDownloadEnabled) { "Downloading infected files is not disallowed." } else { "N/A" }
$auditResult.Status = if ($isDisallowInfectedFileDownloadEnabled) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,44 @@
function Test-EnableDKIM {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains
# Pass if Enabled is True for all domains. Fail if any domain has Enabled set to False.
$dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled
$dkimResult = ($dkimConfig | ForEach-Object { $_.Enabled }) -notcontains $false
$dkimFailedDomains = $dkimConfig | Where-Object { -not $_.Enabled } | ForEach-Object { $_.Domain }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($dkimResult) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.9"
$auditResult.RecDescription = "Ensure that DKIM is enabled for all Exchange Online Domains"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.5"
$auditResult.CISDescription = "Implement DMARC"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $dkimResult
$auditResult.Details = if (-not $dkimResult) { "DKIM not enabled for: $($dkimFailedDomains -join ', ')" } else { "All domains have DKIM enabled" }
$auditResult.FailureReason = if (-not $dkimResult) { "DKIM is not enabled for some domains" } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,45 @@
function Test-ExternalNoControl {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.7 (L1) Ensure external participants can't give or request control
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl
$externalControlRestricted = -not $CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.7"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure external participants can't give or request control"
$auditResult.Result = $externalControlRestricted
$auditResult.Details = "AllowExternalParticipantGiveRequestControl is set to $($CsTeamsMeetingPolicyControl.AllowExternalParticipantGiveRequestControl)"
$auditResult.FailureReason = if ($externalControlRestricted) { "N/A" } else { "External participants can give or request control" }
$auditResult.Status = if ($externalControlRestricted) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,53 @@
function Test-ExternalSharingCalendars {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 1.3.3 (L2) Ensure 'External sharing' of calendars is not available (Automated)
$sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' }
# Check if calendar sharing is disabled in all applicable policies
$isExternalSharingDisabled = $true
$sharingPolicyDetails = @()
foreach ($policy in $sharingPolicies) {
if ($policy.Enabled -eq $true) {
$isExternalSharingDisabled = $false
$sharingPolicyDetails += "$($policy.Name): Enabled"
}
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Rec = "1.3.3"
$auditResult.RecDescription = "Ensure 'External sharing' of calendars is not available"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
# The following IG values are placeholders. Replace with actual values when known.
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.CISControlVer = "v8"
# Placeholder for CIS Control, to be replaced with the actual value when available
$auditResult.CISControl = "4.8"
$auditResult.CISDescription = "Uninstall or Disable Unnecessary Services on Enterprise Assets and Software"
$auditResult.Result = $isExternalSharingDisabled
$auditResult.Details = if ($isExternalSharingDisabled) { "Calendar sharing with external users is disabled." } else { "Enabled Sharing Policies: $($sharingPolicyDetails -join ', ')" }
$auditResult.FailureReason = if ($isExternalSharingDisabled) { "N/A" } else { "Calendar sharing with external users is enabled in one or more policies." }
$auditResult.Status = if ($isExternalSharingDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,46 @@
function Test-GlobalAdminsCount {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 1.1.3 (L1) Ensure that between two and four global admins are designated
# Pass if the count of global admins is between 2 and 4. Fail otherwise.
$globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'"
$globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id
$globalAdminCount = $globalAdmins.AdditionalProperties.Count
$globalAdminUsernames = ($globalAdmins | ForEach-Object { $_.AdditionalProperties["displayName"] }) -join ', '
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "5.1"
$auditResult.CISDescription = "Establish and Maintain an Inventory of Accounts"
$auditResult.Rec = "1.1.3"
$auditResult.ELevel = "E3" # Based on your environment (E3, E5, etc.)
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $true # Set based on the benchmark
$auditResult.IG2 = $true # Set based on the benchmark
$auditResult.IG3 = $true # Set based on the benchmark
$auditResult.RecDescription = "Ensure that between two and four global admins are designated"
$auditResult.Result = $globalAdminCount -ge 2 -and $globalAdminCount -le 4
$auditResult.Details = "Count: $globalAdminCount; Users: $globalAdminUsernames"
$auditResult.FailureReason = if ($globalAdminCount -lt 2) { "Less than 2 global admins: $globalAdminUsernames" } elseif ($globalAdminCount -gt 4) { "More than 4 global admins: $globalAdminUsernames" } else { "N/A" }
$auditResult.Status = if ($globalAdminCount -ge 2 -and $globalAdminCount -le 4) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,41 @@
function Test-GuestAccessExpiration {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.9 (L1) Ensure guest access to a site or OneDrive will expire automatically
$SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays
$isGuestAccessExpirationConfiguredCorrectly = $SPOTenantGuestAccess.ExternalUserExpirationRequired -and $SPOTenantGuestAccess.ExternalUserExpireInDays -le 30
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "7.2.9"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure guest access to a site or OneDrive will expire automatically"
$auditResult.Result = $isGuestAccessExpirationConfiguredCorrectly
$auditResult.Details = "ExternalUserExpirationRequired: $($SPOTenantGuestAccess.ExternalUserExpirationRequired); ExternalUserExpireInDays: $($SPOTenantGuestAccess.ExternalUserExpireInDays)"
$auditResult.FailureReason = if (-not $isGuestAccessExpirationConfiguredCorrectly) { "Guest access expiration is not configured to automatically expire within 30 days or less." } else { "N/A" }
$auditResult.Status = if ($isGuestAccessExpirationConfiguredCorrectly) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,44 @@
function Test-IdentifyExternalEmail {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 6.2.3 (L1) Ensure email from external senders is identified
# Requirement is to have external sender tagging enabled
$externalInOutlook = Get-ExternalInOutlook
$externalTaggingEnabled = ($externalInOutlook | ForEach-Object { $_.Enabled }) -contains $true
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($externalTaggingEnabled) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.2.3"
$auditResult.RecDescription = "Ensure email from external senders is identified"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.Result = $externalTaggingEnabled
$auditResult.Details = "Enabled: $($externalTaggingEnabled); AllowList: $($externalInOutlook.AllowList)"
$auditResult.FailureReason = if (-not $externalTaggingEnabled) { "External sender tagging is disabled" } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,42 @@
function Test-LinkSharingRestrictions {
[CmdletBinding()]
param (
# Define your parameters here
# Test behavior in prod
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.7 (L1) Ensure link sharing is restricted in SharePoint and OneDrive
$SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType
$isLinkSharingRestricted = $SPOTenantLinkSharing.DefaultSharingLinkType -eq 'Direct' # Or 'SpecificPeople' as per the recommendation
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.7"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure link sharing is restricted in SharePoint and OneDrive"
$auditResult.Result = $isLinkSharingRestricted
$auditResult.Details = "DefaultSharingLinkType: $($SPOTenantLinkSharing.DefaultSharingLinkType)"
$auditResult.FailureReason = if (-not $isLinkSharingRestricted) { "Link sharing is not restricted to 'Specific people'. Current setting: $($SPOTenantLinkSharing.DefaultSharingLinkType)" } else { "N/A" }
$auditResult.Status = if ($isLinkSharingRestricted) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,43 @@
function Test-MailTipsEnabled {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 6.5.2 (L2) Ensure MailTips are enabled for end users
$orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold
$allTipsEnabled = $orgConfig.MailTipsAllTipsEnabled -and $orgConfig.MailTipsGroupMetricsEnabled -and $orgConfig.MailTipsLargeAudienceThreshold -eq 25
$externalRecipientsTipsEnabled = $orgConfig.MailTipsExternalRecipientsTipsEnabled
# Since there is no direct CIS Control mapping, the control will be set as not applicable.
$auditResult.CISControl = "0"
$auditResult.CISControlVer = "v8"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "6.5.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure MailTips are enabled for end users"
$auditResult.Result = $allTipsEnabled -and $externalRecipientsTipsEnabled
$auditResult.Details = "MailTipsAllTipsEnabled: $($orgConfig.MailTipsAllTipsEnabled); MailTipsExternalRecipientsTipsEnabled: $($orgConfig.MailTipsExternalRecipientsTipsEnabled); MailTipsGroupMetricsEnabled: $($orgConfig.MailTipsGroupMetricsEnabled); MailTipsLargeAudienceThreshold: $($orgConfig.MailTipsLargeAudienceThreshold)"
$auditResult.FailureReason = if (-not $auditResult.Result) { "One or more MailTips settings are not configured as required." } else { "N/A" }
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,111 @@
function Test-MailboxAuditingE3 {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$e3SkuPartNumbers = @("ENTERPRISEPACK", "OFFICESUBSCRIPTION")
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MoveToDeletedItems", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$auditResult = [CISAuditResult]::new()
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.1.2"
$auditResult.RecDescription = "Ensure mailbox auditing for Office E3 users is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect audit logs."
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$allFailures = @()
$allUsers = Get-AzureADUser -All $true
$processedUsers = @{} # Dictionary to track processed users
}
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) {
$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
}
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
# Mark the user as processed
$processedUsers[$user.UserPrincipalName] = $true
}
}
catch {
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
}
}
$auditResult.Result = $allFailures.Count -eq 0
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
$auditResult.Details = if ($auditResult.Result) { "All Office E3 users have correct mailbox audit settings." } else { $allFailures -join " | " }
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
}
end {
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 '; '
}

View File

@@ -0,0 +1,120 @@
function Test-MailboxAuditingE5 {
[CmdletBinding()]
param ()
begin {
$AdminActions = @("ApplyRecord", "Copy", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$DelegateActions = @("ApplyRecord", "Create", "FolderBind", "HardDelete", "MailItemsAccessed", "Move", "MoveToDeletedItems", "SendAs", "SendOnBehalf", "SoftDelete", "Update", "UpdateFolderPermissions", "UpdateInboxRules")
$OwnerActions = @("ApplyRecord", "Create", "HardDelete", "MailboxLogin", "Move", "MailItemsAccessed", "MoveToDeletedItems", "Send", "SoftDelete", "Update", "UpdateCalendarDelegation", "UpdateFolderPermissions", "UpdateInboxRules")
$auditResult = [CISAuditResult]::new()
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "6.1.3"
$auditResult.RecDescription = "Ensure mailbox auditing for Office E5 users is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "8.2"
$auditResult.CISDescription = "Collect audit logs."
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$allFailures = @()
$allUsers = Get-AzureADUser -All $true
$processedUsers = @{} # Dictionary to track processed users
}
process {
foreach ($user in $allUsers) {
if ($processedUsers.ContainsKey($user.UserPrincipalName)) {
continue
}
try {
# Define SKU Part Numbers for Office E5 licenses
# Define SKU Part Numbers for Office E5 licenses
$e5SkuPartNumbers = @("SPE_E5", "ENTERPRISEPREMIUM", "OFFICEE5")
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.UserPrincipalName
$hasOfficeE5 = ($licenseDetails | Where-Object { $_.SkuPartNumber -in $e5SkuPartNumbers }).Count -gt 0
Write-Verbose "Evaluating user $($user.UserPrincipalName) for Office E5 license."
if ($hasOfficeE5) {
$userUPN = $user.UserPrincipalName
$mailbox = Get-EXOMailbox -Identity $userUPN -PropertySets Audit
$missingActions = @()
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
}
if ($missingActions) {
$formattedActions = Format-MissingActions $missingActions
$allFailures += "$userUPN`: AuditEnabled - True; $formattedActions"
}
else {
Write-Verbose "User $($user.UserPrincipalName) passed the mailbox audit checks."
}
$processedUsers[$user.UserPrincipalName] = $true
}
else {
# Adding verbose output to indicate the user does not have an E5 license
Write-Verbose "User $($user.UserPrincipalName) does not have an Office E5 license."
}
}
catch {
Write-Warning "Could not retrieve license details for user $($user.UserPrincipalName): $_"
}
}
if ($allFailures.Count -eq 0) {
Write-Verbose "All evaluated E5 users have correct mailbox audit settings."
}
$auditResult.Result = $allFailures.Count -eq 0
$auditResult.Status = if ($auditResult.Result) { "Pass" } else { "Fail" }
$auditResult.Details = if ($auditResult.Result) { "All Office E5 users have correct mailbox audit settings." } else { $allFailures -join " | " }
$auditResult.FailureReason = if (-not $auditResult.Result) { "Audit issues detected." } else { "N/A" }
}
end {
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 '; '
}

View File

@@ -0,0 +1,54 @@
function Test-ManagedApprovedPublicGroups {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 1.2.1 (L2) Ensure that only organizationally managed/approved public groups exist (Automated)
$allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility
# Check if there are public groups and if they are organizationally managed/approved
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "1.2.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true # Based on the provided CIS Control image, IG3 is not applicable
$auditResult.RecDescription = "Ensure that only organizationally managed/approved public groups exist"
if ($null -eq $allGroups -or $allGroups.Count -eq 0) {
$auditResult.Result = $true
$auditResult.Details = "No public groups found."
$auditResult.FailureReason = "N/A"
$auditResult.Status = "Pass"
}
else {
$groupDetails = $allGroups | ForEach-Object { $_.DisplayName + " (" + $_.Visibility + ")" }
$detailsString = $groupDetails -join ', '
$auditResult.Result = $false
$auditResult.Details = "Public groups found: $detailsString"
$auditResult.FailureReason = "There are public groups present that are not organizationally managed/approved."
$auditResult.Status = "Fail"
}
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,46 @@
function Test-MeetingChatNoAnonymous {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.5 (L2) Ensure meeting chat does not allow anonymous users
# Name doesn't match profile level in benchmarks either.
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType
$chatAnonDisabled = $CsTeamsMeetingPolicyChat.MeetingChatEnabledType -eq 'EnabledExceptAnonymous'
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.5"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure meeting chat does not allow anonymous users"
$auditResult.Result = $chatAnonDisabled
$auditResult.Details = "MeetingChatEnabledType is set to $($CsTeamsMeetingPolicyChat.MeetingChatEnabledType)"
$auditResult.FailureReason = if ($chatAnonDisabled) { "N/A" } else { "Meeting chat allows anonymous users" }
$auditResult.Status = if ($chatAnonDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,54 @@
function Test-ModernAuthExchangeOnline {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
$auditResults = [CISAuditResult]::new()
# Initialization code
}
process {
try {
# Ensuring the ExchangeOnlineManagement module is available
# 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled
$orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled
# Create an instance of CISAuditResult and populate it
$auditResults.CISControlVer = "v8"
$auditResults.CISControl = "3.10"
$auditResults.CISDescription = "Encrypt Sensitive Data in Transit"
$auditResults.IG1 = $false # As per CIS Control v8 mapping for IG1
$auditResults.IG2 = $true # As per CIS Control v8 mapping for IG2
$auditResults.IG3 = $true # As per CIS Control v8 mapping for IG3
$auditResults.ELevel = "E3" # Based on your environment (E3, E5, etc.)
$auditResults.Profile = "L1"
$auditResults.Rec = "6.5.1"
$auditResults.RecDescription = "Ensure modern authentication for Exchange Online is enabled (Automated)"
$auditResults.Result = $orgConfig.OAuth2ClientProfileEnabled
$auditResults.Details = $auditResults.Details = $orgConfig.Name + " OAuth2ClientProfileEnabled: " + $orgConfig.OAuth2ClientProfileEnabled
$auditResults.FailureReason = if (-not $orgConfig.OAuth2ClientProfileEnabled) { "Modern authentication is disabled" } else { "N/A" }
$auditResults.Status = if ($orgConfig.OAuth2ClientProfileEnabled) { "Pass" } else { "Fail" }
}
catch {
Write-Error "An error occurred while testing modern authentication for Exchange Online: $_"
}
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,39 @@
function Test-ModernAuthSharePoint {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.1 (L1) Ensure modern authentication for SharePoint applications is required
$SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled
$modernAuthForSPRequired = -not $SPOTenant.LegacyAuthProtocolsEnabled
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.10"
$auditResult.CISDescription = "Encrypt Sensitive Data in Transit"
$auditResult.Rec = "7.2.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Modern Authentication for SharePoint Applications"
$auditResult.Result = $modernAuthForSPRequired
$auditResult.Details = "LegacyAuthProtocolsEnabled: $($SPOTenant.LegacyAuthProtocolsEnabled)"
$auditResult.FailureReason = if (-not $modernAuthForSPRequired) { "Legacy authentication protocols are enabled" } else { "N/A" }
$auditResult.Status = if ($modernAuthForSPRequired) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,45 @@
function Test-NoAnonymousMeetingJoin {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.1 (L2) Ensure anonymous users can't join a meeting
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global
$allowAnonymousUsersToJoinMeeting = $teamsMeetingPolicy.AllowAnonymousUsersToJoinMeeting
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure anonymous users can't join a meeting"
$auditResult.Result = -not $allowAnonymousUsersToJoinMeeting
$auditResult.Details = "AllowAnonymousUsersToJoinMeeting is set to $allowAnonymousUsersToJoinMeeting"
$auditResult.FailureReason = if ($allowAnonymousUsersToJoinMeeting) { "Anonymous users are allowed to join meetings" } else { "N/A" }
$auditResult.Status = if (-not $allowAnonymousUsersToJoinMeeting) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,45 @@
function Test-NoAnonymousMeetingStart {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting
$anonymousStartDisabled = -not $CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure anonymous users and dial-in callers can't start a meeting"
$auditResult.Result = $anonymousStartDisabled
$auditResult.Details = "AllowAnonymousUsersToStartMeeting is set to $($CsTeamsMeetingPolicyAnonymous.AllowAnonymousUsersToStartMeeting)"
$auditResult.FailureReason = if ($anonymousStartDisabled) { "N/A" } else { "Anonymous users and dial-in callers can start a meeting" }
$auditResult.Status = if ($anonymousStartDisabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,47 @@
function Test-NoWhitelistDomains {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains
$whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $_.SenderDomainIs -ne $null }
$auditResult.CISControl = "0.0"
$auditResult.CISControlVer = "v8"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "6.2.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure mail transport rules do not whitelist specific domains"
if ($whitelistedRules) {
$ruleDetails = $whitelistedRules | ForEach-Object { "{0}: {1}" -f $_.Name, ($_.SenderDomainIs -join ', ') }
$auditResult.Result = $false
$auditResult.Details = "Whitelisted Rules: $($ruleDetails -join '; ')"
$auditResult.FailureReason = "There are transport rules whitelisting specific domains."
$auditResult.Status = "Fail"
} else {
$auditResult.Result = $true
$auditResult.Details = "No transport rules whitelisting specific domains found."
$auditResult.FailureReason = "N/A"
$auditResult.Status = "Pass"
}
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,52 @@
function Test-NotifyMalwareInternal {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$auditResults = @()
}
process {
# Retrieve all 'Custom' malware filter policies and check notification settings
$malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' }
$policiesToReport = @()
foreach ($policy in $malwareNotifications) {
if ($policy.EnableInternalSenderAdminNotifications -ne $true) {
$policiesToReport += "$($policy.Identity): Notifications Disabled"
}
}
# Determine the result based on the presence of custom policies without notifications
$result = $policiesToReport.Count -eq 0
$details = if ($result) { "All custom malware policies have notifications enabled." } else { "Misconfigured Policies: $($policiesToReport -join ', ')" }
$failureReason = if ($result) { "N/A" } else { "Some custom policies do not have notifications for internal users sending malware enabled." }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.3"
$auditResult.RecDescription = "Ensure notifications for internal users sending malware is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "17.5"
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,41 @@
function Test-OneDriveContentRestrictions {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.4 (L2) Ensure OneDrive content sharing is restricted
$SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability
$isOneDriveSharingRestricted = $SPOTenant.OneDriveSharingCapability -eq 'Disabled'
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.4"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure OneDrive content sharing is restricted"
$auditResult.Result = $isOneDriveSharingRestricted
$auditResult.Details = "OneDriveSharingCapability: $($SPOTenant.OneDriveSharingCapability)"
$auditResult.FailureReason = if (-not $isOneDriveSharingRestricted) { "OneDrive content sharing is not restricted to 'Disabled'. Current setting: $($SPOTenant.OneDriveSharingCapability)" } else { "N/A" }
$auditResult.Status = if ($isOneDriveSharingRestricted) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,41 @@
function Test-OneDriveSyncRestrictions {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.3.2 (L2) Ensure OneDrive sync is restricted for unmanaged devices
$SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList
$isSyncRestricted = $SPOTenantSyncClientRestriction.TenantRestrictionEnabled -and $SPOTenantSyncClientRestriction.AllowedDomainList
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "7.3.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure OneDrive sync is restricted for unmanaged devices"
$auditResult.Result = $isSyncRestricted
$auditResult.Details = "TenantRestrictionEnabled: $($SPOTenantSyncClientRestriction.TenantRestrictionEnabled); AllowedDomainList: $($SPOTenantSyncClientRestriction.AllowedDomainList -join ', ')"
$auditResult.FailureReason = if (-not $isSyncRestricted) { "OneDrive sync is not restricted to managed devices. TenantRestrictionEnabled should be True and AllowedDomainList should contain trusted domains GUIDs." } else { "N/A" }
$auditResult.Status = if ($isSyncRestricted) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,45 @@
function Test-OrgOnlyBypassLobby {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.3 (L1) Ensure only people in my org can bypass the lobby
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers
$lobbyBypassRestricted = $CsTeamsMeetingPolicyLobby.AutoAdmittedUsers -eq 'EveryoneInCompanyExcludingGuests'
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "6.8"
$auditResult.CISDescription = "Define and Maintain Role-Based Access Control"
$auditResult.Rec = "8.5.3"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $true # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure only people in my org can bypass the lobby"
$auditResult.Result = $lobbyBypassRestricted
$auditResult.Details = "AutoAdmittedUsers is set to $($CsTeamsMeetingPolicyLobby.AutoAdmittedUsers)"
$auditResult.FailureReason = if ($lobbyBypassRestricted) { "N/A" } else { "External participants can bypass the lobby" }
$auditResult.Status = if ($lobbyBypassRestricted) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,45 @@
function Test-OrganizersPresent {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.5.6 (L2) Ensure only organizers and co-organizers can present
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode
$presenterRoleRestricted = $CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode -eq 'OrganizerOnlyUserOverride'
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.5.6"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure only organizers and co-organizers can present"
$auditResult.Result = $presenterRoleRestricted
$auditResult.Details = "DesignatedPresenterRoleMode is set to $($CsTeamsMeetingPolicyPresenters.DesignatedPresenterRoleMode)"
$auditResult.FailureReason = if ($presenterRoleRestricted) { "N/A" } else { "Others besides organizers and co-organizers can present" }
$auditResult.Status = if ($presenterRoleRestricted) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,43 @@
function Test-PasswordHashSync {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 5.1.8.1 (L1) Ensure password hash sync is enabled for hybrid deployments
# Pass if OnPremisesSyncEnabled is True. Fail otherwise.
$passwordHashSync = Get-MgOrganization | Select-Object OnPremisesSyncEnabled
$hashSyncResult = $passwordHashSync.OnPremisesSyncEnabled
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($hashSyncResult) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "5.1.8.1"
$auditResult.RecDescription = "Ensure password hash sync is enabled for hybrid deployments"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "6.7"
$auditResult.CISDescription = "Centralize Access Control"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $hashSyncResult
$auditResult.Details = "OnPremisesSyncEnabled: $($passwordHashSync.OnPremisesSyncEnabled)"
$auditResult.FailureReason = if (-not $hashSyncResult) { "Password hash sync for hybrid deployments is not enabled" } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,45 @@
function Test-PasswordNeverExpirePolicy {
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$DomainName # DomainName parameter is now mandatory
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 1.3.1 (L1) Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'
# Pass if PasswordValidityPeriodInDays is 0.
# Fail otherwise.
$passwordPolicy = Get-MgDomain -DomainId $DomainName | Select-Object PasswordValidityPeriodInDays
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Rec = "1.3.1"
$auditResult.RecDescription = "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire'"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "5.2"
$auditResult.CISDescription = "Use Unique Passwords"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true # All are true
$auditResult.Result = $passwordPolicy.PasswordValidityPeriodInDays -eq 0
$auditResult.Details = "Validity Period: $($passwordPolicy.PasswordValidityPeriodInDays) days"
$auditResult.FailureReason = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "N/A" } else { "Password expiration is not set to never expire" }
$auditResult.Status = if ($passwordPolicy.PasswordValidityPeriodInDays -eq 0) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,41 @@
function Test-ReauthWithCode {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.10 (L1) Ensure reauthentication with verification code is restricted
$SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays
$isReauthenticationRestricted = $SPOTenantReauthentication.EmailAttestationRequired -and $SPOTenantReauthentication.EmailAttestationReAuthDays -le 15
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "7.2.10"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure reauthentication with verification code is restricted"
$auditResult.Result = $isReauthenticationRestricted
$auditResult.Details = "EmailAttestationRequired: $($SPOTenantReauthentication.EmailAttestationRequired); EmailAttestationReAuthDays: $($SPOTenantReauthentication.EmailAttestationReAuthDays)"
$auditResult.FailureReason = if (-not $isReauthenticationRestricted) { "Reauthentication with verification code does not require reauthentication within 15 days or less." } else { "N/A" }
$auditResult.Status = if ($isReauthenticationRestricted) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,56 @@
function Test-ReportSecurityInTeams {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.6.1 (L1) Ensure users can report security concerns in Teams
# Connect to Teams PowerShell using Connect-MicrosoftTeams
# Connect to Exchange Online PowerShell using Connect-ExchangeOnline
$CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting
$ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportChatMessageToCustomizedAddressEnabled
$securityReportEnabled = $CsTeamsMessagingPolicy.AllowSecurityEndUserReporting -and
$ReportSubmissionPolicy.ReportJunkToCustomizedAddress -and
$ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress -and
$ReportSubmissionPolicy.ReportPhishToCustomizedAddress -and
$ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.6.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure users can report security concerns in Teams"
$auditResult.Result = $securityReportEnabled
$auditResult.Details = "AllowSecurityEndUserReporting: $($CsTeamsMessagingPolicy.AllowSecurityEndUserReporting); " +
"ReportJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportJunkToCustomizedAddress); " +
"ReportNotJunkToCustomizedAddress: $($ReportSubmissionPolicy.ReportNotJunkToCustomizedAddress); " +
"ReportPhishToCustomizedAddress: $($ReportSubmissionPolicy.ReportPhishToCustomizedAddress); " +
"ReportChatMessageToCustomizedAddressEnabled: $($ReportSubmissionPolicy.ReportChatMessageToCustomizedAddressEnabled)"
$auditResult.FailureReason = if (-not $securityReportEnabled) { "Users cannot report security concerns in Teams due to one or more incorrect settings" } else { "N/A" }
$auditResult.Status = if ($securityReportEnabled) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,70 @@
function Test-RestrictCustomScripts {
[CmdletBinding()]
param (
# Define your parameters here if needed
)
#Limit All
begin {
# .TODO Test behavior in Prod
# Dot source the class script
$auditResults = @()
}
process {
# CIS 2.7 Ensure custom script execution is restricted on site collections
# Pass if DenyAddAndCustomizePages is set to true (Enabled). Fail otherwise.
# Get all site collections and select necessary properties
$SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages
# Find sites where custom scripts are allowed (DenyAddAndCustomizePages is not 'Enabled')
$customScriptAllowedSites = $SPOSitesCustomScript | Where-Object { $_.DenyAddAndCustomizePages -ne 'Enabled' }
# 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 {
"$($_.Title) ($($_.Url)): Custom Script Allowed"
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "2.7"
$auditResult.CISDescription = "Allowlist Authorized Scripts"
$auditResult.Rec = "7.3.4"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure custom script execution is restricted on site collections"
$auditResult.Result = $complianceResult
$auditResult.Details = if (-not $complianceResult) {
$nonCompliantSiteDetails -join "; "
} else {
"All site collections have custom script execution restricted"
}
$auditResult.FailureReason = if (-not $complianceResult) {
"The following site collections allow custom script execution: " + ($nonCompliantSiteDetails -join "; ")
} else {
"N/A"
}
$auditResult.Status = if ($complianceResult) {
"Pass"
} else {
"Fail"
}
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,41 @@
function Test-RestrictExternalSharing {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.3 (L1) Ensure external content sharing is restricted
$SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability
$isRestricted = $SPOTenantSharingCapability.SharingCapability -in @('ExternalUserSharingOnly', 'ExistingExternalUserSharingOnly', 'Disabled')
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.3"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure external content sharing is restricted"
$auditResult.Result = $isRestricted
$auditResult.Details = "SharingCapability: $($SPOTenantSharingCapability.SharingCapability)"
$auditResult.FailureReason = if (-not $isRestricted) { "External content sharing is not adequately restricted. Current setting: $($SPOTenantSharingCapability.SharingCapability)" } else { "N/A" }
$auditResult.Status = if ($isRestricted) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,89 @@
function Test-RestrictOutlookAddins {
[CmdletBinding()]
param (
# Parameters could include credentials or other necessary data
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
$customPolicyFailures = @()
$defaultPolicyFailureDetails = @()
$relevantRoles = @('My Custom Apps', 'My Marketplace Apps', 'My ReadWriteMailbox Apps')
}
process {
# Main functionality
# 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed
# Check all mailboxes for custom policies with unallowed add-ins
$roleAssignmentPolicies = Get-EXOMailbox | Select-Object -Unique RoleAssignmentPolicy
if ($roleAssignmentPolicies.RoleAssignmentPolicy) {
foreach ($policy in $roleAssignmentPolicies) {
if ($policy.RoleAssignmentPolicy) {
$rolePolicyDetails = Get-RoleAssignmentPolicy -Identity $policy.RoleAssignmentPolicy
$foundRoles = $rolePolicyDetails.AssignedRoles | Where-Object { $_ -in $relevantRoles }
if ($foundRoles) {
$customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')"
}
}
}
}
# Check Default Role Assignment Policy
$defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy"
$defaultPolicyRoles = $defaultPolicy.AssignedRoles | Where-Object { $_ -in $relevantRoles }
if ($defaultPolicyRoles) {
$defaultPolicyFailureDetails = $defaultPolicyRoles
}
}
end {
# Prepare result object
$auditResult.Rec = "6.3.1"
$auditResult.CISControl = "9.4"
$auditResult.CISDescription = "Restrict Unnecessary or Unauthorized Browser and Email Client Extensions"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure users installing Outlook add-ins is not allowed"
$detailsString = ""
if ($customPolicyFailures) {
$detailsString += "Custom Policy Failures: | "
# Use pipes or tabs here instead of newlines
$detailsString += ($customPolicyFailures -join " | ")
}
else {
$detailsString += "Custom Policy Failures: None | "
}
$detailsString += "Default Role Assignment Policy: "
if ($defaultPolicyFailureDetails) {
$detailsString += ($defaultPolicyFailureDetails -join ', ')
}
else {
$detailsString += "Compliant"
}
if ($customPolicyFailures -or $defaultPolicyFailureDetails) {
$auditResult.Result = $false
$auditResult.Status = "Fail"
$auditResult.Details = $detailsString
$auditResult.FailureReason = "Unauthorized Outlook add-ins found in custom or default policies."
}
else {
$auditResult.Result = $true
$auditResult.Status = "Pass"
$auditResult.Details = "No unauthorized Outlook add-ins found in custom or default policies."
$auditResult.FailureReason = "N/A"
}
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,48 @@
function Test-RestrictStorageProvidersOutlook {
[CmdletBinding()]
param (
# Parameters can be added here if needed
)
begin {
# Dot source the class script
$auditResult = [CISAuditResult]::new()
}
process {
# 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web
$owaPolicies = Get-OwaMailboxPolicy
$allPoliciesRestricted = $owaPolicies | ForEach-Object { $_.AdditionalStorageProvidersAvailable } | ForEach-Object { -not $_ }
# Create an instance of CISAuditResult and populate it
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "6.5.3"
$auditResult.ELevel = "E3" # Based on your environment
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure additional storage providers are restricted in Outlook on the web"
$auditResult.Result = $allPoliciesRestricted
$auditResult.Details = if($allPoliciesRestricted) {
"All OwaMailbox policies restrict AdditionalStorageProvidersAvailable"
} else {
$nonCompliantPolicies = $owaPolicies | Where-Object { $_.AdditionalStorageProvidersAvailable } | Select-Object -ExpandProperty Name
"Non-compliant OwaMailbox policies: $($nonCompliantPolicies -join ', ')"
}
$auditResult.FailureReason = if(-not $allPoliciesRestricted) { "One or more OwaMailbox policies allow AdditionalStorageProvidersAvailable." } else { "N/A" }
$auditResult.Status = if($allPoliciesRestricted) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}
# Additional helper functions (if any)

View File

@@ -0,0 +1,43 @@
function Test-RestrictTenantCreation {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 5.1.2.3 (L1) Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'
# Pass if AllowedToCreateTenants is False. Fail otherwise.
$tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants
$tenantCreationResult = -not $tenantCreationPolicy.AllowedToCreateTenants
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($tenantCreationResult) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "5.1.2.3"
$auditResult.RecDescription = "Ensure 'Restrict non-admin users from creating tenants' is set to 'Yes'"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.Result = $tenantCreationResult
$auditResult.Details = "AllowedToCreateTenants: $($tenantCreationPolicy.AllowedToCreateTenants)"
$auditResult.FailureReason = if (-not $tenantCreationResult) { "Non-admin users can create tenants" } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,49 @@
function Test-SafeAttachmentsPolicy {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$auditResults = @()
}
process {
# Retrieve all Safe Attachment policies where Enable is set to True
$safeAttachmentPolicies = Get-SafeAttachmentPolicy | Where-Object { $_.Enable -eq $true }
# If there are any enabled policies, the result is Pass. If not, it's Fail.
$result = $safeAttachmentPolicies -ne $null -and $safeAttachmentPolicies.Count -gt 0
$details = if ($result) {
"Enabled Safe Attachments Policies: $($safeAttachmentPolicies.Name -join ', ')"
} else {
"No Safe Attachments Policies are enabled."
}
$failureReason = if ($result) { "N/A" } else { "Safe Attachments policy is not enabled." }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "2.1.4"
$auditResult.RecDescription = "Ensure Safe Attachments policy is enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.7"
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,57 @@
function Test-SafeAttachmentsTeams {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$auditResults = @()
}
process {
# Requires E5 license
# 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
}
# Determine the result based on the ATP policy settings
$result = $null -ne $atpPolicyResult
$details = if ($result) {
"ATP for SharePoint, OneDrive, and Teams is enabled with correct settings."
} else {
"ATP for SharePoint, OneDrive, and Teams is not enabled with correct settings."
}
$failureReason = if ($result) { "N/A" } else { "ATP policy for SharePoint, OneDrive, and Microsoft Teams is not correctly configured." }
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "2.1.5"
$auditResult.RecDescription = "Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "9.7, 10.1"
$auditResult.CISDescription = "Deploy and Maintain Email Server Anti-Malware Protections, Deploy and Maintain Anti-Malware Software"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = $failureReason
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,70 @@
function Test-SafeLinksOfficeApps {
[CmdletBinding()]
param (
# Define your parameters here if needed
)
begin {
# Initialization code
$auditResults = @()
}
process {
# Retrieve all Safe Links policies
$policies = Get-SafeLinksPolicy
# Initialize the details collection
$misconfiguredDetails = @()
foreach ($policy in $policies) {
# Get the detailed configuration of each policy
$policyDetails = Get-SafeLinksPolicy -Identity $policy.Name
# Check each required property and record failures
$failures = @()
if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" }
if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" }
if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" }
if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" }
if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" }
if ($policyDetails.ScanUrls -ne $true) { $failures += "ScanUrls: False" }
if ($policyDetails.EnableForInternalSenders -ne $true) { $failures += "EnableForInternalSenders: False" }
if ($policyDetails.DeliverMessageAfterScan -ne $true) { $failures += "DeliverMessageAfterScan: False" }
if ($policyDetails.DisableUrlRewrite -ne $false) { $failures += "DisableUrlRewrite: True" }
# Only add details for policies that have misconfigurations
if ($failures.Count -gt 0) {
$misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')"
}
}
# Prepare the final result
$result = $misconfiguredDetails.Count -eq 0
$details = if ($result) { "All Safe Links policies are correctly configured." } else { $misconfiguredDetails -join ' | ' }
# Create the audit result object
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($result) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E5"
$auditResult.ProfileLevel = "L2"
$auditResult.Rec = "2.1.1"
$auditResult.RecDescription = "Ensure Safe Links for Office Applications is Enabled"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "10.1"
$auditResult.CISDescription = "Deploy and Maintain Anti-Malware Software"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $result
$auditResult.Details = $details
$auditResult.FailureReason = if ($result) { "N/A" } else { "The following Safe Links policies settings do not meet the recommended configuration: $($misconfiguredDetails -join ' | ')" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,40 @@
function Test-SharePointAADB2B {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 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
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0"
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "7.2.2"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.IG1 = $false
$auditResult.IG2 = $false
$auditResult.IG3 = $false
$auditResult.RecDescription = "Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled"
$auditResult.Result = $SPOTenantAzureADB2B.EnableAzureADB2BIntegration
$auditResult.Details = "EnableAzureADB2BIntegration: $($SPOTenantAzureADB2B.EnableAzureADB2BIntegration)"
$auditResult.FailureReason = if (-not $SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Azure AD B2B integration is not enabled" } else { "N/A" }
$auditResult.Status = if ($SPOTenantAzureADB2B.EnableAzureADB2BIntegration) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,41 @@
function Test-SharePointExternalSharingDomains {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists
$SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList
$isDomainRestrictionConfigured = $SPOTenant.SharingDomainRestrictionMode -eq 'AllowList'
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.6"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure SharePoint external sharing is managed through domain whitelist/blacklists"
$auditResult.Result = $isDomainRestrictionConfigured
$auditResult.Details = "SharingDomainRestrictionMode: $($SPOTenant.SharingDomainRestrictionMode); SharingAllowedDomainList: $($SPOTenant.SharingAllowedDomainList)"
$auditResult.FailureReason = if (-not $isDomainRestrictionConfigured) { "Domain restrictions for SharePoint external sharing are not configured to 'AllowList'. Current setting: $($SPOTenant.SharingDomainRestrictionMode)" } else { "N/A" }
$auditResult.Status = if ($isDomainRestrictionConfigured) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,41 @@
function Test-SharePointGuestsItemSharing {
[CmdletBinding()]
param (
# Define your parameters here
)
begin {
# Initialization code
$auditResult = [CISAuditResult]::new()
}
process {
# 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own
$SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing
$isGuestResharingPrevented = $SPOTenant.PreventExternalUsersFromResharing
# Populate the auditResult object with the required properties
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "7.2.5"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.RecDescription = "Ensure that SharePoint guest users cannot share items they don't own"
$auditResult.Result = $isGuestResharingPrevented
$auditResult.Details = "PreventExternalUsersFromResharing: $isGuestResharingPrevented"
$auditResult.FailureReason = if (-not $isGuestResharingPrevented) { "Guest users can reshare items they don't own." } else { "N/A" }
$auditResult.Status = if ($isGuestResharingPrevented) { "Pass" } else { "Fail" }
}
end {
# Return auditResult
return $auditResult
}
}

View File

@@ -0,0 +1,56 @@
function Test-SpamPolicyAdminNotify {
[CmdletBinding()]
param (
# Parameters can be added if needed
)
begin {
$auditResults = @()
}
process {
# Get the default hosted outbound spam filter policy
$hostedOutboundSpamFilterPolicy = Get-HostedOutboundSpamFilterPolicy | Where-Object { $_.IsDefault -eq $true }
# Check if both settings are enabled
$bccSuspiciousOutboundMailEnabled = $hostedOutboundSpamFilterPolicy.BccSuspiciousOutboundMail
$notifyOutboundSpamEnabled = $hostedOutboundSpamFilterPolicy.NotifyOutboundSpam
$areSettingsEnabled = $bccSuspiciousOutboundMailEnabled -and $notifyOutboundSpamEnabled
# Prepare failure details if any setting is not enabled
$failureDetails = @()
if (-not $bccSuspiciousOutboundMailEnabled) {
$failureDetails += "BccSuspiciousOutboundMail is not enabled."
}
if (-not $notifyOutboundSpamEnabled) {
$failureDetails += "NotifyOutboundSpam is not enabled."
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.Status = if ($areSettingsEnabled) { "Pass" } else { "Fail" }
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L1"
$auditResult.Rec = "2.1.6"
$auditResult.RecDescription = "Ensure Exchange Online Spam Policies are set to notify administrators"
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "17.5"
$auditResult.CISDescription = "Assign Key Roles and Responsibilities"
$auditResult.IG1 = $false
$auditResult.IG2 = $true
$auditResult.IG3 = $true
$auditResult.Result = $areSettingsEnabled
$auditResult.Details = if ($areSettingsEnabled) { "Both BccSuspiciousOutboundMail and NotifyOutboundSpam are enabled." } else { $failureDetails -join ' ' }
$auditResult.FailureReason = if (-not $areSettingsEnabled) { "One or both spam policies are not set to notify administrators." } else { "N/A" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,52 @@
function Test-TeamsExternalAccess {
[CmdletBinding()]
param (
# Parameters can be defined here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center
# Connect to Teams PowerShell using Connect-MicrosoftTeams
$externalAccessConfig = Get-CsTenantFederationConfiguration
$allowedDomainsLimited = $false
if ($externalAccessConfig.AllowFederatedUsers -and $externalAccessConfig.AllowedDomains -and $externalAccessConfig.AllowedDomains.AllowedDomain.Count -gt 0) {
$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
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "0.0" # The control is Explicitly Not Mapped as per the image provided
$auditResult.CISDescription = "Explicitly Not Mapped"
$auditResult.Rec = "8.2.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $false # Set based on the CIS Controls image
$auditResult.IG2 = $false # Set based on the CIS Controls image
$auditResult.IG3 = $false # Set based on the CIS Controls image
$auditResult.RecDescription = "Ensure 'external access' is restricted in the Teams admin center"
$auditResult.Result = $isCompliant
$auditResult.Details = "AllowTeamsConsumer: $($externalAccessConfig.AllowTeamsConsumer); AllowPublicUsers: $($externalAccessConfig.AllowPublicUsers); AllowFederatedUsers: $($externalAccessConfig.AllowFederatedUsers); AllowedDomains limited: $allowedDomainsLimited"
$auditResult.FailureReason = if (-not $isCompliant) { "One or more external access configurations are not compliant." } else { "N/A" }
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}

View File

@@ -0,0 +1,56 @@
function Test-TeamsExternalFileSharing {
[CmdletBinding()]
param (
# Parameters can be added here if needed
)
begin {
# Dot source the class script
$auditResults = @()
}
process {
# 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
# This list must be defined according to your organization's approved cloud storage services
$approvedProviders = @("AllowDropBox", "AllowBox", "AllowGoogleDrive", "AllowShareFile", "AllowEgnyte")
$clientConfig = Get-CsTeamsClientConfiguration
$isCompliant = $true
$nonCompliantProviders = @()
foreach ($provider in $approvedProviders) {
if (-not $clientConfig.$provider) {
$isCompliant = $false
$nonCompliantProviders += $provider
}
}
# Create an instance of CISAuditResult and populate it
$auditResult = [CISAuditResult]::new()
$auditResult.CISControlVer = "v8"
$auditResult.CISControl = "3.3"
$auditResult.CISDescription = "Configure Data Access Control Lists"
$auditResult.Rec = "8.1.1"
$auditResult.ELevel = "E3"
$auditResult.ProfileLevel = "L2"
$auditResult.IG1 = $true # Set based on the benchmark
$auditResult.IG2 = $true # Set based on the benchmark
$auditResult.IG3 = $true # Set based on the benchmark
$auditResult.RecDescription = "Ensure external file sharing in Teams is enabled for only approved cloud storage services"
$auditResult.Result = $isCompliant
$auditResult.Details = if (-not $isCompliant) { "Non-approved providers enabled: $($nonCompliantProviders -join ', ')" } else { "All cloud storage services are approved providers" }
$auditResult.FailureReason = if (-not $isCompliant) { "The following non-approved providers are enabled: $($nonCompliantProviders -join ', ')" } else { "N/A" }
$auditResult.Status = if ($isCompliant) { "Pass" } else { "Fail" }
$auditResults += $auditResult
}
end {
# Return auditResults
return $auditResults
}
}