From d7d16ff0b50abc7ffc26f02ef6d665f17786bfb7 Mon Sep 17 00:00:00 2001 From: DrIOS <58635327+DrIOSX@users.noreply.github.com> Date: Sat, 3 Aug 2024 18:52:46 -0500 Subject: [PATCH] add: App Authentication test --- .../Classes/CISAuthenticationParameters.ps1 | 43 + source/Private/Connect-M365Suite.ps1 | 132 +-- source/Private/Disconnect-M365Suite.ps1 | 10 +- source/Private/Get-CISExoOutput.ps1 | 825 +++++++++--------- source/Private/Get-CISMSTeamsOutput.ps1 | 577 ++++++------ source/Private/Get-CISMgOutput.ps1 | 140 +-- source/Private/Get-CISSpoOutput.ps1 | 229 +---- source/Private/Get-RequiredModule.ps1 | 25 +- source/Private/Get-UniqueConnection.ps1 | 5 +- source/Private/Measure-AuditResult.ps1 | 4 +- source/Public/Invoke-M365SecurityAudit.ps1 | 13 +- .../New-CISAuthenticationParameters.ps1 | 61 ++ source/helper/TestDefinitions.csv | 2 +- .../tests/Test-BlockSharedMailboxSignIn.ps1 | 4 +- 14 files changed, 1035 insertions(+), 1035 deletions(-) create mode 100644 source/Classes/CISAuthenticationParameters.ps1 create mode 100644 source/Public/New-CISAuthenticationParameters.ps1 diff --git a/source/Classes/CISAuthenticationParameters.ps1 b/source/Classes/CISAuthenticationParameters.ps1 new file mode 100644 index 0000000..9b10b95 --- /dev/null +++ b/source/Classes/CISAuthenticationParameters.ps1 @@ -0,0 +1,43 @@ +class CISAuthenticationParameters { + [string]$ClientCertThumbPrint + [string]$ClientId + [string]$TenantId + [string]$OnMicrosoftUrl + [string]$SpAdminUrl + + # Constructor with validation + CISAuthenticationParameters( + [string]$ClientCertThumbPrint, + [string]$ClientId, + [string]$TenantId, + [string]$OnMicrosoftUrl, + [string]$SpAdminUrl + ) { + # Validate ClientCertThumbPrint + if (-not $ClientCertThumbPrint -or $ClientCertThumbPrint.Length -ne 40 -or $ClientCertThumbPrint -notmatch '^[0-9a-fA-F]{40}$') { + throw [ArgumentException]::new("ClientCertThumbPrint must be a 40-character hexadecimal string.") + } + # Validate ClientId + if (-not $ClientId -or $ClientId -notmatch '^[0-9a-fA-F\-]{36}$') { + throw [ArgumentException]::new("ClientId must be a valid GUID in the format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'.") + } + # Validate TenantId + if (-not $TenantId -or $TenantId -notmatch '^[0-9a-fA-F\-]{36}$') { + throw [ArgumentException]::new("TenantId must be a valid GUID in the format 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'.") + } + # Validate OnMicrosoftUrl + if (-not $OnMicrosoftUrl -or $OnMicrosoftUrl -notmatch '^[a-zA-Z0-9]+\.onmicrosoft\.com$') { + throw [ArgumentException]::new("OnMicrosoftUrl must be in the format 'example.onmicrosoft.com'.") + } + # Validate SpAdminUrl + if (-not $SpAdminUrl -or $SpAdminUrl -notmatch '^https:\/\/[a-zA-Z0-9\-]+\-admin\.sharepoint\.com$') { + throw [ArgumentException]::new("SpAdminUrl must be in the format 'https://[name]-admin.sharepoint.com'.") + } + # Assign validated properties + $this.ClientCertThumbPrint = $ClientCertThumbPrint + $this.ClientId = $ClientId + $this.TenantId = $TenantId + $this.OnMicrosoftUrl = $OnMicrosoftUrl + $this.SpAdminUrl = $SpAdminUrl + } +} diff --git a/source/Private/Connect-M365Suite.ps1 b/source/Private/Connect-M365Suite.ps1 index b429357..11ee279 100644 --- a/source/Private/Connect-M365Suite.ps1 +++ b/source/Private/Connect-M365Suite.ps1 @@ -2,13 +2,21 @@ function Connect-M365Suite { [OutputType([void])] [CmdletBinding()] param ( - [Parameter(Mandatory = $false)] + [Parameter( + Mandatory = $false + )] [string]$TenantAdminUrl, - - [Parameter(Mandatory)] + [Parameter( + Mandatory = $false + )] + [CISAuthenticationParameters]$AuthParams, # Custom authentication parameters + [Parameter( + Mandatory + )] [string[]]$RequiredConnections, - - [Parameter(Mandatory = $false)] + [Parameter( + Mandatory = $false + )] [switch]$SkipConfirmation ) if (!$SkipConfirmation) { @@ -19,87 +27,90 @@ function Connect-M365Suite { } $tenantInfo = @() $connectedServices = @() - try { - if ($RequiredConnections -contains "AzureAD" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "AzureAD | EXO | Microsoft Graph") { - Write-Verbose "Connecting to Azure Active Directory..." - Connect-AzureAD -WarningAction SilentlyContinue | Out-Null - $tenantDetails = Get-AzureADTenantDetail -WarningAction SilentlyContinue - $tenantInfo += [PSCustomObject]@{ - Service = "Azure Active Directory" - TenantName = $tenantDetails.DisplayName - TenantID = $tenantDetails.ObjectId - } - $connectedServices += "AzureAD" - Write-Verbose "Successfully connected to Azure Active Directory." - } - if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "EXO | Microsoft Graph") { - Write-Verbose "Connecting to Microsoft Graph with scopes: Directory.Read.All, Domain.Read.All, Policy.Read.All, Organization.Read.All" - try { + Write-Verbose "Connecting to Microsoft Graph" + if ($AuthParams) { + # Use application-based authentication + Connect-MgGraph -CertificateThumbprint $AuthParams.ClientCertThumbPrint -AppId $AuthParams.ClientId -TenantId $AuthParams.TenantId -NoWelcome | Out-Null + } + else { + # Use interactive authentication with scopes Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null - $graphOrgDetails = Get-MgOrganization - $tenantInfo += [PSCustomObject]@{ - Service = "Microsoft Graph" - TenantName = $graphOrgDetails.DisplayName - TenantID = $graphOrgDetails.Id - } - $connectedServices += "Microsoft Graph" - Write-Verbose "Successfully connected to Microsoft Graph with specified scopes." } - catch { - Write-Verbose "Failed to connect to MgGraph, attempting device auth." - Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -UseDeviceCode -NoWelcome | Out-Null - $graphOrgDetails = Get-MgOrganization - $tenantInfo += [PSCustomObject]@{ - Service = "Microsoft Graph" - TenantName = $graphOrgDetails.DisplayName - TenantID = $graphOrgDetails.Id - } - $connectedServices += "Microsoft Graph" - Write-Verbose "Successfully connected to Microsoft Graph with specified scopes." + $graphOrgDetails = Get-MgOrganization + $tenantInfo += [PSCustomObject]@{ + Service = "Microsoft Graph" + TenantName = $graphOrgDetails.DisplayName + TenantID = $graphOrgDetails.Id } + $connectedServices += "Microsoft Graph" + Write-Verbose "Successfully connected to Microsoft Graph.`n" } - if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "EXO | Microsoft Graph") { Write-Verbose "Connecting to Exchange Online..." - Connect-ExchangeOnline -ShowBanner:$false | Out-Null + if ($AuthParams) { + # Use application-based authentication + Connect-ExchangeOnline -AppId $AuthParams.ClientId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -Organization $AuthParams.OnMicrosoftUrl -ShowBanner:$false | Out-Null + } + else { + # Use interactive authentication + Connect-ExchangeOnline -ShowBanner:$false | Out-Null + } $exoTenant = (Get-OrganizationConfig).Identity $tenantInfo += [PSCustomObject]@{ - Service = "Exchange Online" + Service = "Exchange Online" TenantName = $exoTenant - TenantID = "N/A" + TenantID = "N/A" } $connectedServices += "EXO" - Write-Verbose "Successfully connected to Exchange Online." + Write-Verbose "Successfully connected to Exchange Online.`n" } - if ($RequiredConnections -contains "SPO") { Write-Verbose "Connecting to SharePoint Online..." - Connect-SPOService -Url $TenantAdminUrl | Out-Null - $spoContext = Get-SPOCrossTenantHostUrl - $tenantName = Get-UrlLine -Output $spoContext + if ($AuthParams) { + # Use application-based authentication + Connect-PnPOnline -Url $AuthParams.SpAdminUrl -ClientId $AuthParams.ClientId -Tenant $AuthParams.OnMicrosoftUrl -Thumbprint $AuthParams.ClientCertThumbPrint | Out-Null + } + else { + # Use interactive authentication + Connect-SPOService -Url $TenantAdminUrl | Out-Null + } + # Assuming that Get-SPOCrossTenantHostUrl and Get-UrlLine are valid commands in your context + if ($AuthParams) { + $spoContext = Get-PnPSite + $tenantName = $spoContext.Url + } + else { + $spoContext = Get-SPOCrossTenantHostUrl + $tenantName = Get-UrlLine -Output $spoContext + } $tenantInfo += [PSCustomObject]@{ - Service = "SharePoint Online" + Service = "SharePoint Online" TenantName = $tenantName } $connectedServices += "SPO" - Write-Verbose "Successfully connected to SharePoint Online." + Write-Verbose "Successfully connected to SharePoint Online.`n" } - if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") { Write-Verbose "Connecting to Microsoft Teams..." - Connect-MicrosoftTeams | Out-Null + if ($AuthParams) { + # Use application-based authentication + Connect-MicrosoftTeams -TenantId $AuthParams.TenantId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -ApplicationId $AuthParams.ClientId | Out-Null + } + else { + # Use interactive authentication + Connect-MicrosoftTeams | Out-Null + } $teamsTenantDetails = Get-CsTenant $tenantInfo += [PSCustomObject]@{ - Service = "Microsoft Teams" + Service = "Microsoft Teams" TenantName = $teamsTenantDetails.DisplayName - TenantID = $teamsTenantDetails.TenantId + TenantID = $teamsTenantDetails.TenantId } $connectedServices += "Microsoft Teams" - Write-Verbose "Successfully connected to Microsoft Teams." + Write-Verbose "Successfully connected to Microsoft Teams.`n" } - # Display tenant information and confirm with the user if (-not $SkipConfirmation) { Write-Verbose "Connected to the following tenants:" @@ -109,7 +120,7 @@ function Connect-M365Suite { #Write-Verbose "Tenant ID: $($tenant.TenantID)" } $confirmation = Read-Host "Do you want to proceed with these connections? (Y/N)" - if ($confirmation -notlike 'Y') { + if ($confirmation -notLike 'Y') { Write-Verbose "Connection setup aborted by user." Disconnect-M365Suite -RequiredConnections $connectedServices throw "User aborted connection setup." @@ -117,10 +128,9 @@ function Connect-M365Suite { } } catch { + $CatchError = $_ $VerbosePreference = "Continue" - Write-Verbose "There was an error establishing one or more connections: $_" - throw $_ + throw $CatchError } - $VerbosePreference = "Continue" } diff --git a/source/Private/Disconnect-M365Suite.ps1 b/source/Private/Disconnect-M365Suite.ps1 index 6cbfff2..f52d820 100644 --- a/source/Private/Disconnect-M365Suite.ps1 +++ b/source/Private/Disconnect-M365Suite.ps1 @@ -38,8 +38,14 @@ function Disconnect-M365Suite { try { if ($RequiredConnections -contains "SPO") { - Write-Verbose "Disconnecting from SharePoint Online..." - Disconnect-SPOService | Out-Null + if (($script:PnpAuth)) { + Write-Verbose "Disconnecting from PnPOnline..." + Disconnect-PnPOnline | Out-Null + } + else { + Write-Verbose "Disconnecting from SharePoint Online..." + Disconnect-SPOService | Out-Null + } } } catch { diff --git a/source/Private/Get-CISExoOutput.ps1 b/source/Private/Get-CISExoOutput.ps1 index 272521f..4d7df94 100644 --- a/source/Private/Get-CISExoOutput.ps1 +++ b/source/Private/Get-CISExoOutput.ps1 @@ -46,437 +46,442 @@ function Get-CISExoOutput { #> } process { - Write-Verbose "Get-CISExoOutput: Retuning data for Rec: $Rec" - switch ($Rec) { - '1.2.2' { - # Test-BlockSharedMailboxSignIn.ps1 - $MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox - # [object[]] - # $MBX mock object: - <# - $MBX = @( - [PSCustomObject]@{ - UserPrincipalName = "SMBuser1@domain.com" - ExternalDirectoryObjectId = "123e4567-e89b-12d3-a456-426614174000" - }, - [PSCustomObject]@{ - UserPrincipalName = "SMBuser2@domain.com" - ExternalDirectoryObjectId = "987e6543-21ba-12d3-a456-426614174000" - }, - [PSCustomObject]@{ - UserPrincipalName = "SMBuser3@domain.com" - ExternalDirectoryObjectId = "abcddcba-98fe-76dc-a456-426614174000" - } - ) - #> - return $MBX.ExternalDirectoryObjectId - } - '1.3.3' { - # Test-ExternalSharingCalendars.ps1 - # Step: Retrieve sharing policies related to calendar sharing - # $sharingPolicies Mock Object - <# - $sharingPolicies = [PSCustomObject]@{ - Name = "Default Sharing Policy" - Domains = @("Anonymous:CalendarSharingFreeBusySimple") - Enabled = $true - Default = $true - } - #> - $sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' } - # [psobject[]] - return $sharingPolicies - } - '1.3.6' { - # Test-CustomerLockbox.ps1 - # Step: Retrieve the organization configuration (Condition C: Pass/Fail) - # $orgConfig Mock Object: - <# - # return $orgConfig - $orgConfig = [PSCustomObject]@{ - CustomerLockBoxEnabled = $true - } - #> - $orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled - $customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled - # [bool] - return $customerLockboxEnabled - } - '2.1.1' { - # Test-SafeLinksOfficeApps.ps1 - if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) { - # 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled - # Retrieve all Safe Links policies - # $policies Mock Object: + try { + Write-Verbose "Get-CISExoOutput: Retuning data for Rec: $Rec" + switch ($Rec) { + '1.2.2' { + # Test-BlockSharedMailboxSignIn.ps1 + $MBX = Get-EXOMailbox -RecipientTypeDetails SharedMailbox + # [object[]] + # $MBX mock object: <# - $policies = @( + $MBX = @( [PSCustomObject]@{ - Name = "PolicyOne" - EnableSafeLinksForEmail = $true - EnableSafeLinksForTeams = $true - EnableSafeLinksForOffice = $true - TrackClicks = $true - AllowClickThrough = $false + UserPrincipalName = "SMBuser1@domain.com" + ExternalDirectoryObjectId = "123e4567-e89b-12d3-a456-426614174000" }, [PSCustomObject]@{ - Name = "PolicyTwo" - EnableSafeLinksForEmail = $true - EnableSafeLinksForTeams = $true - EnableSafeLinksForOffice = $true - TrackClicks = $true - AllowClickThrough = $true + UserPrincipalName = "SMBuser2@domain.com" + ExternalDirectoryObjectId = "987e6543-21ba-12d3-a456-426614174000" }, [PSCustomObject]@{ - Name = "PolicyThree" - EnableSafeLinksForEmail = $true - EnableSafeLinksForTeams = $true - EnableSafeLinksForOffice = $true - TrackClicks = $true - AllowClickThrough = $false + UserPrincipalName = "SMBuser3@domain.com" + ExternalDirectoryObjectId = "abcddcba-98fe-76dc-a456-426614174000" } ) #> - $policies = Get-SafeLinksPolicy - # Initialize the details collection - $misconfiguredDetails = @() - foreach ($policy in $policies) { - # Get the detailed configuration of each policy - $policyDetails = $policy #Get-SafeLinksPolicy -Identity $policy.Name - # Check each required property and record failures - # Condition A: Checking policy settings - $failures = @() - if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On - if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On - if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On - if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On - if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off - # Only add details for policies that have misconfigurations - if ($failures.Count -gt 0) { - $misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')" - } - } - # [object[]] - return $misconfiguredDetails + return $MBX.ExternalDirectoryObjectId } - else { - return 1 - } - } - '2.1.2' { - # Test-CommonAttachmentFilter.ps1 - # 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled - # Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center. - # Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`. - # Retrieve the attachment filter policy - # $attachmentFilter Mock Object - <# - $attachmentFilter = [PSCustomObject]@{ - EnableFileFilter = $true - } - #> - $attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter - $result = $attachmentFilter.EnableFileFilter - # [bool] - return $result - } - '2.1.3' { - # Test-NotifyMalwareInternal.ps1 - # 2.1.3 Ensure notifications for internal users sending malware is Enabled - # Retrieve all 'Custom' malware filter policies and check notification settings - # $malwareNotifications Mock Object - <# - $malwareNotifications = @( - [PSCustomObject]@{ - Identity = "Default" - EnableInternalSenderAdminNotifications = $true - RecommendedPolicyType = "Custom" - }, - [PSCustomObject]@{ - Identity = "Anti-malware-Policy" - EnableInternalSenderAdminNotifications = $true - RecommendedPolicyType = "Custom" - } - ) - #> - $malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' } - # [object[]] - return $malwareNotifications - } - '2.1.4' { - # Test-SafeAttachmentsPolicy.ps1 - if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) { - # Retrieve all Safe Attachment policies where Enable is set to True - # Check if ErrorAction needed below - # $safeAttachmentPolicies Mock Object: + '1.3.3' { + # Test-ExternalSharingCalendars.ps1 + # Step: Retrieve sharing policies related to calendar sharing + # $sharingPolicies Mock Object <# - $safeAttachmentPolicies = @( - [PSCustomObject]@{ - Policy = "Strict Preset Security Policy" - Action = "Block" - QuarantineTag = "AdminOnlyAccessPolicy" - Redirect = $false - Enabled = $true - } - ) + $sharingPolicies = [PSCustomObject]@{ + Name = "Default Sharing Policy" + Domains = @("Anonymous:CalendarSharingFreeBusySimple") + Enabled = $true + Default = $true + } #> - $safeAttachmentPolicies = Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue | Where-Object { $_.Enable -eq $true } - $safeAttachmentRules = Get-SafeAttachmentRule - # [object[]] - return $safeAttachmentPolicies, $safeAttachmentRules - else { - return 1,1 - } - } - } - '2.1.5' { - # Test-SafeAttachmentsTeams.ps1 - if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) { - # 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled - # Retrieve the ATP policies for Office 365 and check Safe Attachments settings - $atpPolicies = Get-AtpPolicyForO365 - # Check if the required ATP policies are enabled - # $atpPolicyResult Mock Object: - <# - $atpPolicyResult = @( - [PSCustomObject]@{ - Name = "Default" - EnableATPForSPOTeamsODB = $true - EnableSafeDocs = $true - AllowSafeDocsOpen = $false - } - ) - #> - $atpPolicyResult = $atpPolicies | Where-Object { - $_.EnableATPForSPOTeamsODB -eq $true -and - $_.EnableSafeDocs -eq $true -and - $_.AllowSafeDocsOpen -eq $false - } + $sharingPolicies = Get-SharingPolicy | Where-Object { $_.Domains -like '*CalendarSharing*' } # [psobject[]] - return $atpPolicyResult + return $sharingPolicies } - else { - return 1 - } - } - '2.1.6' { - # Test-SpamPolicyAdminNotify.ps1 - # Retrieve the hosted outbound spam filter policies - # $spamPolicies Mock Object: - <# - # Mock data representing multiple spam filter policies - $spamPolicies = @( - [PSCustomObject]@{ - Name = "Default" - IsDefault = $true - NotifyOutboundSpam = $true - BccSuspiciousOutboundMail = $true - NotifyOutboundSpamRecipients = "admin@example.com" - BccSuspiciousOutboundAdditionalRecipients = "bccadmin@example.com" - }, - [PSCustomObject]@{ - Name = "Custom Policy 1" - IsDefault = $false - NotifyOutboundSpam = $false - BccSuspiciousOutboundMail = $true - NotifyOutboundSpamRecipients = "" - BccSuspiciousOutboundAdditionalRecipients = "" - }, - [PSCustomObject]@{ - Name = "Custom Policy 2" - IsDefault = $false - NotifyOutboundSpam = $true - BccSuspiciousOutboundMail = $false - NotifyOutboundSpamRecipients = "notify@example.com" - BccSuspiciousOutboundAdditionalRecipients = "bccnotify@example.com" + '1.3.6' { + # Test-CustomerLockbox.ps1 + # Step: Retrieve the organization configuration (Condition C: Pass/Fail) + # $orgConfig Mock Object: + <# + # return $orgConfig + $orgConfig = [PSCustomObject]@{ + CustomerLockBoxEnabled = $true } - ) - #> - $spamPolicies = Get-HostedOutboundSpamFilterPolicy - return $spamPolicies - } - '2.1.7' { - # Test-AntiPhishingPolicy.ps1 - <# - $antiPhishPolicies = @( - [PSCustomObject]@{ - Identity = "Strict Preset Security Policy" - Enabled = $true - PhishThresholdLevel = 4 - EnableMailboxIntelligenceProtection = $true - EnableMailboxIntelligence = $true - EnableSpoofIntelligence = $true - TargetedUsersToProtect = "John Doe;jdoe@contoso.net, Jane Does;janedoe@contoso.net" - }, - [PSCustomObject]@{ - Identity = "Office365 AntiPhish Default" - Enabled = $true - PhishThresholdLevel = 2 - EnableMailboxIntelligenceProtection = $true - EnableMailboxIntelligence = $true - EnableSpoofIntelligence = $true - TargetedUsersToProtect = $null # Assuming it targets all users as it's the default - }, - [PSCustomObject]@{ - Identity = "Admin" - Enabled = $true - PhishThresholdLevel = 2 - EnableMailboxIntelligenceProtection = $true - EnableMailboxIntelligence = $true - EnableSpoofIntelligence = $true - TargetedUsersToProtect = $null # Assuming it targets all users - }, - [PSCustomObject]@{ - Identity = "Standard Preset Security Policy" - Enabled = $true - PhishThresholdLevel = 3 - EnableMailboxIntelligenceProtection = $true - EnableMailboxIntelligence = $true - EnableSpoofIntelligence = $true - TargetedUsersToProtect = $null # Assuming it targets all users - } - ) - #> - $antiPhishPolicies = Get-AntiPhishPolicy - return $antiPhishPolicies - } - '2.1.9' { - # Test-EnableDKIM.ps1 - # 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains - # Retrieve DKIM configuration for all domains - $dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled - # [object[]] - return $dkimConfig - } - '3.1.1' { - # Test-AuditLogSearch.ps1 - # 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled - # Retrieve the audit log configuration - $auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled - # - $auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled - # [bool] - return $auditLogResult - } - '6.1.1' { - # Test-AuditDisabledFalse.ps1 - # 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False' - # Retrieve the AuditDisabled configuration (Condition B) - $auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled - # [bool] - $auditNotDisabled = -not $auditDisabledConfig.AuditDisabled - return $auditNotDisabled - } - '6.1.2' { - # Test-MailboxAuditingE3.ps1 - $mailboxes = Get-EXOMailbox -PropertySets Audit - # [object[]] - return $mailboxes - } - '6.1.3' { - # Test-MailboxAuditingE5.ps1 - $mailboxes = Get-EXOMailbox -PropertySets Audit - # [object[]] - return $mailboxes - } - '6.2.1' { - # Test-BlockMailForwarding.ps1 - # 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled - # Step 1: Retrieve the transport rules that redirect messages - $transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo } - if ($null -eq $transportRules) { - $transportRules = 1 + #> + $orgConfig = Get-OrganizationConfig | Select-Object CustomerLockBoxEnabled + $customerLockboxEnabled = $orgConfig.CustomerLockBoxEnabled + # [bool] + return $customerLockboxEnabled } - # Step 2: Check all anti-spam outbound policies - $outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy - $nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' } - return $transportRules, $nonCompliantSpamPolicies - } - '6.2.2' { - # Test-NoWhitelistDomains.ps1 - # 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains - # Retrieve transport rules that whitelist specific domains - # Condition A: Checking for transport rules that whitelist specific domains - # [object[]] - $whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs } - return $whitelistedRules - } - '6.2.3' { - # Test-IdentifyExternalEmail.ps1 - # 6.2.3 (L1) Ensure email from external senders is identified - # Retrieve external sender tagging configuration - # [object[]] - $externalInOutlook = Get-ExternalInOutlook - return $externalInOutlook - } - '6.3.1' { - # Test-RestrictOutlookAddins.ps1 - # 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed - $customPolicyFailures = @() - # 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 } - # Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users. - if ($foundRoles) { - $customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')" + '2.1.1' { + # Test-SafeLinksOfficeApps.ps1 + if (Get-Command Get-SafeLinksPolicy -ErrorAction SilentlyContinue) { + # 2.1.1 (L2) Ensure Safe Links for Office Applications is Enabled + # Retrieve all Safe Links policies + # $policies Mock Object: + <# + $policies = @( + [PSCustomObject]@{ + Name = "PolicyOne" + EnableSafeLinksForEmail = $true + EnableSafeLinksForTeams = $true + EnableSafeLinksForOffice = $true + TrackClicks = $true + AllowClickThrough = $false + }, + [PSCustomObject]@{ + Name = "PolicyTwo" + EnableSafeLinksForEmail = $true + EnableSafeLinksForTeams = $true + EnableSafeLinksForOffice = $true + TrackClicks = $true + AllowClickThrough = $true + }, + [PSCustomObject]@{ + Name = "PolicyThree" + EnableSafeLinksForEmail = $true + EnableSafeLinksForTeams = $true + EnableSafeLinksForOffice = $true + TrackClicks = $true + AllowClickThrough = $false + } + ) + #> + $policies = Get-SafeLinksPolicy + # Initialize the details collection + $misconfiguredDetails = @() + foreach ($policy in $policies) { + # Get the detailed configuration of each policy + $policyDetails = $policy #Get-SafeLinksPolicy -Identity $policy.Name + # Check each required property and record failures + # Condition A: Checking policy settings + $failures = @() + if ($policyDetails.EnableSafeLinksForEmail -ne $true) { $failures += "EnableSafeLinksForEmail: False" } # Email: On + if ($policyDetails.EnableSafeLinksForTeams -ne $true) { $failures += "EnableSafeLinksForTeams: False" } # Teams: On + if ($policyDetails.EnableSafeLinksForOffice -ne $true) { $failures += "EnableSafeLinksForOffice: False" } # Office 365 Apps: On + if ($policyDetails.TrackClicks -ne $true) { $failures += "TrackClicks: False" } # Click protection settings: On + if ($policyDetails.AllowClickThrough -ne $false) { $failures += "AllowClickThrough: True" } # Do not track when users click safe links: Off + # Only add details for policies that have misconfigurations + if ($failures.Count -gt 0) { + $misconfiguredDetails += "Policy: $($policy.Name); Failures: $($failures -join ', ')" + } + } + # [object[]] + return $misconfiguredDetails + } + else { + return 1 + } + } + '2.1.2' { + # Test-CommonAttachmentFilter.ps1 + # 2.1.2 (L1) Ensure the Common Attachment Types Filter is enabled + # Condition A: The Common Attachment Types Filter is enabled in the Microsoft 365 Security & Compliance Center. + # Condition B: Using Exchange Online PowerShell, verify that the `EnableFileFilter` property of the default malware filter policy is set to `True`. + # Retrieve the attachment filter policy + # $attachmentFilter Mock Object + <# + $attachmentFilter = [PSCustomObject]@{ + EnableFileFilter = $true + } + #> + $attachmentFilter = Get-MalwareFilterPolicy -Identity Default | Select-Object EnableFileFilter + $result = $attachmentFilter.EnableFileFilter + # [bool] + return $result + } + '2.1.3' { + # Test-NotifyMalwareInternal.ps1 + # 2.1.3 Ensure notifications for internal users sending malware is Enabled + # Retrieve all 'Custom' malware filter policies and check notification settings + # $malwareNotifications Mock Object + <# + $malwareNotifications = @( + [PSCustomObject]@{ + Identity = "Default" + EnableInternalSenderAdminNotifications = $true + RecommendedPolicyType = "Custom" + }, + [PSCustomObject]@{ + Identity = "Anti-malware-Policy" + EnableInternalSenderAdminNotifications = $true + RecommendedPolicyType = "Custom" + } + ) + #> + $malwareNotifications = Get-MalwareFilterPolicy | Where-Object { $_.RecommendedPolicyType -eq 'Custom' } + # [object[]] + return $malwareNotifications + } + '2.1.4' { + # Test-SafeAttachmentsPolicy.ps1 + if (Get-Command Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue) { + # Retrieve all Safe Attachment policies where Enable is set to True + # Check if ErrorAction needed below + # $safeAttachmentPolicies Mock Object: + <# + $safeAttachmentPolicies = @( + [PSCustomObject]@{ + Policy = "Strict Preset Security Policy" + Action = "Block" + QuarantineTag = "AdminOnlyAccessPolicy" + Redirect = $false + Enabled = $true + } + ) + #> + $safeAttachmentPolicies = Get-SafeAttachmentPolicy -ErrorAction SilentlyContinue | Where-Object { $_.Enable -eq $true } + $safeAttachmentRules = Get-SafeAttachmentRule + # [object[]] + return $safeAttachmentPolicies, $safeAttachmentRules + else { + return 1,1 + } + } + } + '2.1.5' { + # Test-SafeAttachmentsTeams.ps1 + if (Get-Command Get-AtpPolicyForO365 -ErrorAction SilentlyContinue) { + # 2.1.5 (L2) Ensure Safe Attachments for SharePoint, OneDrive, and Microsoft Teams is Enabled + # Retrieve the ATP policies for Office 365 and check Safe Attachments settings + $atpPolicies = Get-AtpPolicyForO365 + # Check if the required ATP policies are enabled + # $atpPolicyResult Mock Object: + <# + $atpPolicyResult = @( + [PSCustomObject]@{ + Name = "Default" + EnableATPForSPOTeamsODB = $true + EnableSafeDocs = $true + AllowSafeDocsOpen = $false + } + ) + #> + $atpPolicyResult = $atpPolicies | Where-Object { + $_.EnableATPForSPOTeamsODB -eq $true -and + $_.EnableSafeDocs -eq $true -and + $_.AllowSafeDocsOpen -eq $false + } + # [psobject[]] + return $atpPolicyResult + } + else { + return 1 + } + } + '2.1.6' { + # Test-SpamPolicyAdminNotify.ps1 + # Retrieve the hosted outbound spam filter policies + # $spamPolicies Mock Object: + <# + # Mock data representing multiple spam filter policies + $spamPolicies = @( + [PSCustomObject]@{ + Name = "Default" + IsDefault = $true + NotifyOutboundSpam = $true + BccSuspiciousOutboundMail = $true + NotifyOutboundSpamRecipients = "admin@example.com" + BccSuspiciousOutboundAdditionalRecipients = "bccadmin@example.com" + }, + [PSCustomObject]@{ + Name = "Custom Policy 1" + IsDefault = $false + NotifyOutboundSpam = $false + BccSuspiciousOutboundMail = $true + NotifyOutboundSpamRecipients = "" + BccSuspiciousOutboundAdditionalRecipients = "" + }, + [PSCustomObject]@{ + Name = "Custom Policy 2" + IsDefault = $false + NotifyOutboundSpam = $true + BccSuspiciousOutboundMail = $false + NotifyOutboundSpamRecipients = "notify@example.com" + BccSuspiciousOutboundAdditionalRecipients = "bccnotify@example.com" + } + ) + #> + $spamPolicies = Get-HostedOutboundSpamFilterPolicy + return $spamPolicies + } + '2.1.7' { + # Test-AntiPhishingPolicy.ps1 + <# + $antiPhishPolicies = @( + [PSCustomObject]@{ + Identity = "Strict Preset Security Policy" + Enabled = $true + PhishThresholdLevel = 4 + EnableMailboxIntelligenceProtection = $true + EnableMailboxIntelligence = $true + EnableSpoofIntelligence = $true + TargetedUsersToProtect = "John Doe;jdoe@contoso.net, Jane Does;janedoe@contoso.net" + }, + [PSCustomObject]@{ + Identity = "Office365 AntiPhish Default" + Enabled = $true + PhishThresholdLevel = 2 + EnableMailboxIntelligenceProtection = $true + EnableMailboxIntelligence = $true + EnableSpoofIntelligence = $true + TargetedUsersToProtect = $null # Assuming it targets all users as it's the default + }, + [PSCustomObject]@{ + Identity = "Admin" + Enabled = $true + PhishThresholdLevel = 2 + EnableMailboxIntelligenceProtection = $true + EnableMailboxIntelligence = $true + EnableSpoofIntelligence = $true + TargetedUsersToProtect = $null # Assuming it targets all users + }, + [PSCustomObject]@{ + Identity = "Standard Preset Security Policy" + Enabled = $true + PhishThresholdLevel = 3 + EnableMailboxIntelligenceProtection = $true + EnableMailboxIntelligence = $true + EnableSpoofIntelligence = $true + TargetedUsersToProtect = $null # Assuming it targets all users + } + ) + #> + $antiPhishPolicies = Get-AntiPhishPolicy + return $antiPhishPolicies + } + '2.1.9' { + # Test-EnableDKIM.ps1 + # 2.1.9 (L1) Ensure DKIM is enabled for all Exchange Online Domains + # Retrieve DKIM configuration for all domains + $dkimConfig = Get-DkimSigningConfig | Select-Object Domain, Enabled + # [object[]] + return $dkimConfig + } + '3.1.1' { + # Test-AuditLogSearch.ps1 + # 3.1.1 (L1) Ensure Microsoft 365 audit log search is Enabled + # Retrieve the audit log configuration + $auditLogConfig = Get-AdminAuditLogConfig | Select-Object UnifiedAuditLogIngestionEnabled + # + $auditLogResult = $auditLogConfig.UnifiedAuditLogIngestionEnabled + # [bool] + return $auditLogResult + } + '6.1.1' { + # Test-AuditDisabledFalse.ps1 + # 6.1.1 (L1) Ensure 'AuditDisabled' organizationally is set to 'False' + # Retrieve the AuditDisabled configuration (Condition B) + $auditDisabledConfig = Get-OrganizationConfig | Select-Object AuditDisabled + # [bool] + $auditNotDisabled = -not $auditDisabledConfig.AuditDisabled + return $auditNotDisabled + } + '6.1.2' { + # Test-MailboxAuditingE3.ps1 + $mailboxes = Get-EXOMailbox -PropertySets Audit + # [object[]] + return $mailboxes + } + '6.1.3' { + # Test-MailboxAuditingE5.ps1 + $mailboxes = Get-EXOMailbox -PropertySets Audit + # [object[]] + return $mailboxes + } + '6.2.1' { + # Test-BlockMailForwarding.ps1 + # 6.2.1 (L1) Ensure all forms of mail forwarding are blocked and/or disabled + # Step 1: Retrieve the transport rules that redirect messages + $transportRules = Get-TransportRule | Where-Object { $null -ne $_.RedirectMessageTo } + if ($null -eq $transportRules) { + $transportRules = 1 + } + # Step 2: Check all anti-spam outbound policies + $outboundSpamPolicies = Get-HostedOutboundSpamFilterPolicy + $nonCompliantSpamPolicies = $outboundSpamPolicies | Where-Object { $_.AutoForwardingMode -ne 'Off' } + return $transportRules, $nonCompliantSpamPolicies + } + '6.2.2' { + # Test-NoWhitelistDomains.ps1 + # 6.2.2 (L1) Ensure mail transport rules do not whitelist specific domains + # Retrieve transport rules that whitelist specific domains + # Condition A: Checking for transport rules that whitelist specific domains + # [object[]] + $whitelistedRules = Get-TransportRule | Where-Object { $_.SetSCL -eq -1 -and $null -ne $_.SenderDomainIs } + return $whitelistedRules + } + '6.2.3' { + # Test-IdentifyExternalEmail.ps1 + # 6.2.3 (L1) Ensure email from external senders is identified + # Retrieve external sender tagging configuration + # [object[]] + $externalInOutlook = Get-ExternalInOutlook + return $externalInOutlook + } + '6.3.1' { + # Test-RestrictOutlookAddins.ps1 + # 6.3.1 (L2) Ensure users installing Outlook add-ins is not allowed + $customPolicyFailures = @() + # 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 } + # Condition B: Using PowerShell, verify that MyCustomApps, MyMarketplaceApps, and MyReadWriteMailboxApps are not assigned to users. + if ($foundRoles) { + $customPolicyFailures += "Policy: $($policy.RoleAssignmentPolicy): Roles: $($foundRoles -join ', ')" + } } } } + # Check Default Role Assignment Policy + $defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy" + return $customPolicyFailures, $defaultPolicy } - # Check Default Role Assignment Policy - $defaultPolicy = Get-RoleAssignmentPolicy "Default Role Assignment Policy" - return $customPolicyFailures, $defaultPolicy + '6.5.1' { + # Test-ModernAuthExchangeOnline.ps1 + # Ensuring the ExchangeOnlineManagement module is available + # 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled + # Check modern authentication setting in Exchange Online configuration (Condition A and B) + $orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled + return $orgConfig + } + '6.5.2' { + # Test-MailTipsEnabled.ps1 + # 6.5.2 (L2) Ensure MailTips are enabled for end users + # Retrieve organization configuration for MailTips settings + # [object] + $orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold + return $orgConfig + } + '6.5.3' { + # Test-RestrictStorageProvidersOutlook.ps1 + # 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web + # Retrieve all OwaMailbox policies + # [object[]] + $owaPolicies = Get-OwaMailboxPolicy + return $owaPolicies + } + '8.6.1' { + # Test-ReportSecurityInTeams.ps1 + # 8.6.1 (L1) Ensure users can report security concerns in Teams + # Retrieve the necessary settings for Teams and Exchange Online + # Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal. + # Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses. + # $ReportSubmissionPolicy Mock Object + <# + $ReportSubmissionPolicy = [PSCustomObject]@{ + ReportJunkToCustomizedAddress = $true + ReportNotJunkToCustomizedAddress = $true + ReportPhishToCustomizedAddress = $true + ReportJunkAddresses = @('security@example.com') + ReportNotJunkAddresses = @('security@example.com') + ReportPhishAddresses = @('security@example.com') + ReportChatMessageEnabled = $false + ReportChatMessageToCustomizedAddressEnabled = $false + } + #> + $ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportJunkAddresses, ReportNotJunkAddresses, ReportPhishAddresses, ReportChatMessageEnabled, ReportChatMessageToCustomizedAddressEnabled + return $ReportSubmissionPolicy + } + default { throw "No match found for test: $Rec" } } - '6.5.1' { - # Test-ModernAuthExchangeOnline.ps1 - # Ensuring the ExchangeOnlineManagement module is available - # 6.5.1 (L1) Ensure modern authentication for Exchange Online is enabled - # Check modern authentication setting in Exchange Online configuration (Condition A and B) - $orgConfig = Get-OrganizationConfig | Select-Object -Property Name, OAuth2ClientProfileEnabled - return $orgConfig - } - '6.5.2' { - # Test-MailTipsEnabled.ps1 - # 6.5.2 (L2) Ensure MailTips are enabled for end users - # Retrieve organization configuration for MailTips settings - # [object] - $orgConfig = Get-OrganizationConfig | Select-Object MailTipsAllTipsEnabled, MailTipsExternalRecipientsTipsEnabled, MailTipsGroupMetricsEnabled, MailTipsLargeAudienceThreshold - return $orgConfig - } - '6.5.3' { - # Test-RestrictStorageProvidersOutlook.ps1 - # 6.5.3 (L2) Ensure additional storage providers are restricted in Outlook on the web - # Retrieve all OwaMailbox policies - # [object[]] - $owaPolicies = Get-OwaMailboxPolicy - return $owaPolicies - } - '8.6.1' { - # Test-ReportSecurityInTeams.ps1 - # 8.6.1 (L1) Ensure users can report security concerns in Teams - # Retrieve the necessary settings for Teams and Exchange Online - # Condition B: Verify that 'Monitor reported messages in Microsoft Teams' is checked in the Microsoft 365 Defender portal. - # Condition C: Ensure the 'Send reported messages to' setting in the Microsoft 365 Defender portal is set to 'My reporting mailbox only' with the correct report email addresses. - # $ReportSubmissionPolicy Mock Object - <# - $ReportSubmissionPolicy = [PSCustomObject]@{ - ReportJunkToCustomizedAddress = $true - ReportNotJunkToCustomizedAddress = $true - ReportPhishToCustomizedAddress = $true - ReportJunkAddresses = @('security@example.com') - ReportNotJunkAddresses = @('security@example.com') - ReportPhishAddresses = @('security@example.com') - ReportChatMessageEnabled = $false - ReportChatMessageToCustomizedAddressEnabled = $false - } - #> - $ReportSubmissionPolicy = Get-ReportSubmissionPolicy | Select-Object -Property ReportJunkToCustomizedAddress, ReportNotJunkToCustomizedAddress, ReportPhishToCustomizedAddress, ReportJunkAddresses, ReportNotJunkAddresses, ReportPhishAddresses, ReportChatMessageEnabled, ReportChatMessageToCustomizedAddressEnabled - return $ReportSubmissionPolicy - } - default { throw "No match found for test: $Rec" } + } + catch { + throw "Get-CISExoOutput: `n$_" } } end { diff --git a/source/Private/Get-CISMSTeamsOutput.ps1 b/source/Private/Get-CISMSTeamsOutput.ps1 index 2122797..1d315d8 100644 --- a/source/Private/Get-CISMSTeamsOutput.ps1 +++ b/source/Private/Get-CISMSTeamsOutput.ps1 @@ -34,296 +34,301 @@ function Get-CISMSTeamsOutput { #> } process { - Write-Verbose "Get-CISMSTeamsOutput: Retuning data for Rec: $Rec" - switch ($Rec) { - '8.1.1' { - # Test-TeamsExternalFileSharing.ps1 - # 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 + try { + Write-Verbose "Get-CISMSTeamsOutput: Retuning data for Rec: $Rec" + switch ($Rec) { + '8.1.1' { + # Test-TeamsExternalFileSharing.ps1 + # 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 - # Condition A: The `AllowDropbox` setting is set to `False`. - # Condition B: The `AllowBox` setting is set to `False`. - # Condition C: The `AllowGoogleDrive` setting is set to `False`. - # Condition D: The `AllowShareFile` setting is set to `False`. - # Condition E: The `AllowEgnyte` setting is set to `False`. + # Condition A: The `AllowDropbox` setting is set to `False`. + # Condition B: The `AllowBox` setting is set to `False`. + # Condition C: The `AllowGoogleDrive` setting is set to `False`. + # Condition D: The `AllowShareFile` setting is set to `False`. + # Condition E: The `AllowEgnyte` setting is set to `False`. - # 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 - # Add option for approved providers. - $clientConfig = Get-CsTeamsClientConfiguration - return $clientConfig - } - '8.1.2' { - # Test-BlockChannelEmails.ps1 - # 8.1.2 (L1) Ensure users can't send emails to a channel email address - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `AllowEmailIntoChannel` setting in Teams is set to `False`. - # - Condition B: The setting `Users can send emails to a channel email address` is set to `Off` in the Teams admin center. - # - Condition C: Verification using PowerShell confirms that the `AllowEmailIntoChannel` setting is disabled. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AllowEmailIntoChannel` setting in Teams is not set to `False`. - # - Condition B: The setting `Users can send emails to a channel email address` is not set to `Off` in the Teams admin center. - # - Condition C: Verification using PowerShell indicates that the `AllowEmailIntoChannel` setting is enabled. + # 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 + # Add option for approved providers. + $clientConfig = Get-CsTeamsClientConfiguration + return $clientConfig + } + '8.1.2' { + # Test-BlockChannelEmails.ps1 + # 8.1.2 (L1) Ensure users can't send emails to a channel email address + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `AllowEmailIntoChannel` setting in Teams is set to `False`. + # - Condition B: The setting `Users can send emails to a channel email address` is set to `Off` in the Teams admin center. + # - Condition C: Verification using PowerShell confirms that the `AllowEmailIntoChannel` setting is disabled. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `AllowEmailIntoChannel` setting in Teams is not set to `False`. + # - Condition B: The setting `Users can send emails to a channel email address` is not set to `Off` in the Teams admin center. + # - Condition C: Verification using PowerShell indicates that the `AllowEmailIntoChannel` setting is enabled. - # Retrieve Teams client configuration - $teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global - return $teamsClientConfig + # Retrieve Teams client configuration + $teamsClientConfig = Get-CsTeamsClientConfiguration -Identity Global + return $teamsClientConfig + } + '8.2.1' { + # Test-TeamsExternalAccess.ps1 + # 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `AllowTeamsConsumer` setting is `False`. + # - Condition B: The `AllowPublicUsers` setting is `False`. + # - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `AllowTeamsConsumer` setting is not `False`. + # - Condition B: The `AllowPublicUsers` setting is not `False`. + # - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly. + # Connect to Teams PowerShell using Connect-MicrosoftTeams + # $externalAccessConfig Mock Object + <# + $externalAccessConfig = [PSCustomObject]@{ + Identity = 'Global' + AllowedDomains = 'AllowAllKnownDomains' + BlockedDomains = @() + AllowFederatedUsers = $true + AllowPublicUsers = $true + AllowTeamsConsumer = $true + AllowTeamsConsumerInbound = $true + } + $ApprovedFederatedDomains = @('msn.com', 'google.com') + $externalAccessConfig = [PSCustomObject]@{ + Identity = 'Global' + AllowedDomains = @('msn.com', 'google.com') + BlockedDomains = @() + AllowFederatedUsers = $true + AllowPublicUsers = $false + AllowTeamsConsumer = $false + AllowTeamsConsumerInbound = $true + } + #> + $externalAccessConfig = Get-CsTenantFederationConfiguration + return $externalAccessConfig + } + '8.5.1' { + # Test-NoAnonymousMeetingJoin.ps1 + # 8.5.1 (L2) Ensure anonymous users can't join a meeting + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: `AllowAnonymousUsersToJoinMeeting` is set to `False`. + # - Condition B: Verification using the UI confirms that `Anonymous users can join a meeting` is set to `Off` in the Global meeting policy. + # - Condition C: PowerShell command output indicates that anonymous users are not allowed to join meetings. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`. + # - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy. + # - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings. + # Connect to Teams PowerShell using Connect-MicrosoftTeams + # $teamsMeetingPolicy Mock Object + <# + $teamsMeetingPolicy = [PSCustomObject]@{ + AllowAnonymousUsersToJoinMeeting = $true + } + #> + $teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global + return $teamsMeetingPolicy + } + '8.5.2' { + # Test-NoAnonymousMeetingStart.ps1 + # 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is set to `False`. + # - Condition B: The setting for anonymous users and dial-in callers starting a meeting is configured to ensure they must wait in the lobby. + # - Condition C: Verification using the UI confirms that the setting `Anonymous users and dial-in callers can start a meeting` is set to `Off`. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`. + # - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby. + # - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`. + # Connect to Teams PowerShell using Connect-MicrosoftTeams + # $CsTeamsMeetingPolicyAnonymous Mock Object + <# + $CsTeamsMeetingPolicyAnonymous = [PSCustomObject]@{ + AllowAnonymousUsersToStartMeeting = $true + } + #> + # Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings + $CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting + return $CsTeamsMeetingPolicyAnonymous + } + '8.5.3' { + # Test-OrgOnlyBypassLobby.ps1 + # 8.5.3 (L1) Ensure only people in my org can bypass the lobby + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is set to `EveryoneInCompanyExcludingGuests`. + # - Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI. + # - Condition C: Verification using the Microsoft Teams admin center confirms that the meeting join & lobby settings are configured as recommended. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`. + # - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI. + # - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended. + # Connect to Teams PowerShell using Connect-MicrosoftTeams + # Retrieve the Teams meeting policy for lobby bypass settings + # $CsTeamsMeetingPolicyLobby Mock Object + <# + $CsTeamsMeetingPolicyLobby = [PSCustomObject]@{ + AutoAdmittedUsers = "OrganizerOnly" + } + #> + $CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers + return $CsTeamsMeetingPolicyLobby + } + '8.5.4' { + # Test-DialInBypassLobby.ps1 + # 8.5.4 (L1) Ensure users dialing in can't bypass the lobby + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is set to `False`. + # - Condition B: Verification using the UI in the Microsoft Teams admin center confirms that "People dialing in can't bypass the lobby" is set to `Off`. + # - Condition C: Ensure that individuals who dial in by phone must wait in the lobby until admitted by a meeting organizer, co-organizer, or presenter. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`. + # - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`. + # - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby. + # Retrieve Teams meeting policy for PSTN users + # $CsTeamsMeetingPolicyPSTN Mock Object + <# + $CsTeamsMeetingPolicyPSTN = [PSCustomObject]@{ + AllowPSTNUsersToBypassLobby = $true + } + #> + $CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby + return $CsTeamsMeetingPolicyPSTN + } + '8.5.5' { + # Test-MeetingChatNoAnonymous.ps1 + # 8.5.5 (L2) Ensure meeting chat does not allow anonymous users + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `MeetingChatEnabledType` setting in Teams is set to `EnabledExceptAnonymous`. + # - Condition B: The setting for meeting chat is configured to allow chat for everyone except anonymous users. + # - Condition C: Verification using the Teams Admin Center confirms that the meeting chat settings are configured as recommended. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`. + # - Condition B: The setting for meeting chat allows chat for anonymous users. + # - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended. + # Retrieve the Teams meeting policy for meeting chat + # $CsTeamsMeetingPolicyChat Mock Object + <# + $CsTeamsMeetingPolicyChat = [PSCustomObject]@{ + MeetingChatEnabledType = "Enabled" + } + #> + $CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType + return $CsTeamsMeetingPolicyChat + } + '8.5.6' { + # Test-OrganizersPresent.ps1 + # 8.5.6 (L2) Ensure only organizers and co-organizers can present + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is set to `OrganizerOnlyUserOverride`. + # - Condition B: Verification using the Teams admin center confirms that the setting "Who can present" is configured to "Only organizers and co-organizers". + # - Condition C: Verification using PowerShell confirms that the `DesignatedPresenterRoleMode` is set to `OrganizerOnlyUserOverride`. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`. + # - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers". + # - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`. + # Retrieve the Teams meeting policy for presenters + # $CsTeamsMeetingPolicyPresenters Mock Object + <# + $CsTeamsMeetingPolicyPresenters = [PSCustomObject]@{ + DesignatedPresenterRoleMode = "Enabled" + } + #> + $CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode + return $CsTeamsMeetingPolicyPresenters + } + '8.5.7' { + # Test-ExternalNoControl.ps1 + # 8.5.7 (L1) Ensure external participants can't give or request control + # + # Validate test for a pass: + # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. + # - Specific conditions to check: + # - Condition A: Ensure the `AllowExternalParticipantGiveRequestControl` setting in Teams is set to `False`. + # - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command. + # - Condition C: Verification using the UI confirms that external participants are unable to give or request control. + # + # Validate test for a fail: + # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. + # - Specific conditions to check: + # - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`. + # - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command. + # - Condition C: Verification using the UI indicates that external participants can give or request control. + # Retrieve Teams meeting policy for external participant control + # $CsTeamsMeetingPolicyControl Mock Object + <# + $CsTeamsMeetingPolicyControl = [PSCustomObject]@{ + AllowExternalParticipantGiveRequestControl = $true + } + #> + $CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl + return $CsTeamsMeetingPolicyControl + } + '8.6.1' { + # Test-ReportSecurityInTeams.ps1 + # 8.6.1 (L1) Ensure users can report security concerns in Teams + # Retrieve the necessary settings for Teams and Exchange Online + # Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'. + # $CsTeamsMessagingPolicy Mock Object + <# + $CsTeamsMessagingPolicy = [PSCustomObject]@{ + AllowSecurityEndUserReporting = $true + } + #> + $CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting + return $CsTeamsMessagingPolicy + } + default { throw "No match found for test: $Rec" } } - '8.2.1' { - # Test-TeamsExternalAccess.ps1 - # 8.2.1 (L1) Ensure 'external access' is restricted in the Teams admin center - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `AllowTeamsConsumer` setting is `False`. - # - Condition B: The `AllowPublicUsers` setting is `False`. - # - Condition C: The `AllowFederatedUsers` setting is `False` or, if `True`, the `AllowedDomains` contains only authorized domain names. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AllowTeamsConsumer` setting is not `False`. - # - Condition B: The `AllowPublicUsers` setting is not `False`. - # - Condition C: The `AllowFederatedUsers` setting is `True` and the `AllowedDomains` contains unauthorized domain names or is not configured correctly. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # $externalAccessConfig Mock Object - <# - $externalAccessConfig = [PSCustomObject]@{ - Identity = 'Global' - AllowedDomains = 'AllowAllKnownDomains' - BlockedDomains = @() - AllowFederatedUsers = $true - AllowPublicUsers = $true - AllowTeamsConsumer = $true - AllowTeamsConsumerInbound = $true - } - $ApprovedFederatedDomains = @('msn.com', 'google.com') - $externalAccessConfig = [PSCustomObject]@{ - Identity = 'Global' - AllowedDomains = @('msn.com', 'google.com') - BlockedDomains = @() - AllowFederatedUsers = $true - AllowPublicUsers = $false - AllowTeamsConsumer = $false - AllowTeamsConsumerInbound = $true - } - #> - $externalAccessConfig = Get-CsTenantFederationConfiguration - return $externalAccessConfig - } - '8.5.1' { - # Test-NoAnonymousMeetingJoin.ps1 - # 8.5.1 (L2) Ensure anonymous users can't join a meeting - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: `AllowAnonymousUsersToJoinMeeting` is set to `False`. - # - Condition B: Verification using the UI confirms that `Anonymous users can join a meeting` is set to `Off` in the Global meeting policy. - # - Condition C: PowerShell command output indicates that anonymous users are not allowed to join meetings. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: `AllowAnonymousUsersToJoinMeeting` is not set to `False`. - # - Condition B: Verification using the UI shows that `Anonymous users can join a meeting` is not set to `Off` in the Global meeting policy. - # - Condition C: PowerShell command output indicates that anonymous users are allowed to join meetings. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # $teamsMeetingPolicy Mock Object - <# - $teamsMeetingPolicy = [PSCustomObject]@{ - AllowAnonymousUsersToJoinMeeting = $true - } - #> - $teamsMeetingPolicy = Get-CsTeamsMeetingPolicy -Identity Global - return $teamsMeetingPolicy - } - '8.5.2' { - # Test-NoAnonymousMeetingStart.ps1 - # 8.5.2 (L1) Ensure anonymous users and dial-in callers can't start a meeting - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is set to `False`. - # - Condition B: The setting for anonymous users and dial-in callers starting a meeting is configured to ensure they must wait in the lobby. - # - Condition C: Verification using the UI confirms that the setting `Anonymous users and dial-in callers can start a meeting` is set to `Off`. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AllowAnonymousUsersToStartMeeting` setting in the Teams admin center is not set to `False`. - # - Condition B: The setting for anonymous users and dial-in callers starting a meeting allows them to bypass the lobby. - # - Condition C: Verification using the UI indicates that the setting `Anonymous users and dial-in callers can start a meeting` is not set to `Off`. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # $CsTeamsMeetingPolicyAnonymous Mock Object - <# - $CsTeamsMeetingPolicyAnonymous = [PSCustomObject]@{ - AllowAnonymousUsersToStartMeeting = $true - } - #> - # Retrieve the Teams meeting policy for the global scope and check if anonymous users can start meetings - $CsTeamsMeetingPolicyAnonymous = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowAnonymousUsersToStartMeeting - return $CsTeamsMeetingPolicyAnonymous - } - '8.5.3' { - # Test-OrgOnlyBypassLobby.ps1 - # 8.5.3 (L1) Ensure only people in my org can bypass the lobby - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is set to `EveryoneInCompanyExcludingGuests`. - # - Condition B: The setting for "Who can bypass the lobby" is configured to "People in my org" using the UI. - # - Condition C: Verification using the Microsoft Teams admin center confirms that the meeting join & lobby settings are configured as recommended. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AutoAdmittedUsers` setting in the Teams meeting policy is not set to `EveryoneInCompanyExcludingGuests`. - # - Condition B: The setting for "Who can bypass the lobby" is not configured to "People in my org" using the UI. - # - Condition C: Verification using the Microsoft Teams admin center indicates that the meeting join & lobby settings are not configured as recommended. - # Connect to Teams PowerShell using Connect-MicrosoftTeams - # Retrieve the Teams meeting policy for lobby bypass settings - # $CsTeamsMeetingPolicyLobby Mock Object - <# - $CsTeamsMeetingPolicyLobby = [PSCustomObject]@{ - AutoAdmittedUsers = "OrganizerOnly" - } - #> - $CsTeamsMeetingPolicyLobby = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AutoAdmittedUsers - return $CsTeamsMeetingPolicyLobby - } - '8.5.4' { - # Test-DialInBypassLobby.ps1 - # 8.5.4 (L1) Ensure users dialing in can't bypass the lobby - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is set to `False`. - # - Condition B: Verification using the UI in the Microsoft Teams admin center confirms that "People dialing in can't bypass the lobby" is set to `Off`. - # - Condition C: Ensure that individuals who dial in by phone must wait in the lobby until admitted by a meeting organizer, co-organizer, or presenter. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AllowPSTNUsersToBypassLobby` setting in the Global Teams meeting policy is not set to `False`. - # - Condition B: Verification using the UI in the Microsoft Teams admin center shows that "People dialing in can't bypass the lobby" is not set to `Off`. - # - Condition C: Individuals who dial in by phone are able to join the meeting directly without waiting in the lobby. - # Retrieve Teams meeting policy for PSTN users - # $CsTeamsMeetingPolicyPSTN Mock Object - <# - $CsTeamsMeetingPolicyPSTN = [PSCustomObject]@{ - AllowPSTNUsersToBypassLobby = $true - } - #> - $CsTeamsMeetingPolicyPSTN = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowPSTNUsersToBypassLobby - return $CsTeamsMeetingPolicyPSTN - } - '8.5.5' { - # Test-MeetingChatNoAnonymous.ps1 - # 8.5.5 (L2) Ensure meeting chat does not allow anonymous users - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `MeetingChatEnabledType` setting in Teams is set to `EnabledExceptAnonymous`. - # - Condition B: The setting for meeting chat is configured to allow chat for everyone except anonymous users. - # - Condition C: Verification using the Teams Admin Center confirms that the meeting chat settings are configured as recommended. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `MeetingChatEnabledType` setting in Teams is not set to `EnabledExceptAnonymous`. - # - Condition B: The setting for meeting chat allows chat for anonymous users. - # - Condition C: Verification using the Teams Admin Center indicates that the meeting chat settings are not configured as recommended. - # Retrieve the Teams meeting policy for meeting chat - # $CsTeamsMeetingPolicyChat Mock Object - <# - $CsTeamsMeetingPolicyChat = [PSCustomObject]@{ - MeetingChatEnabledType = "Enabled" - } - #> - $CsTeamsMeetingPolicyChat = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property MeetingChatEnabledType - return $CsTeamsMeetingPolicyChat - } - '8.5.6' { - # Test-OrganizersPresent.ps1 - # 8.5.6 (L2) Ensure only organizers and co-organizers can present - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is set to `OrganizerOnlyUserOverride`. - # - Condition B: Verification using the Teams admin center confirms that the setting "Who can present" is configured to "Only organizers and co-organizers". - # - Condition C: Verification using PowerShell confirms that the `DesignatedPresenterRoleMode` is set to `OrganizerOnlyUserOverride`. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `DesignatedPresenterRoleMode` setting in the Teams meeting policy is not set to `OrganizerOnlyUserOverride`. - # - Condition B: Verification using the Teams admin center indicates that the setting "Who can present" is not configured to "Only organizers and co-organizers". - # - Condition C: Verification using PowerShell indicates that the `DesignatedPresenterRoleMode` is not set to `OrganizerOnlyUserOverride`. - # Retrieve the Teams meeting policy for presenters - # $CsTeamsMeetingPolicyPresenters Mock Object - <# - $CsTeamsMeetingPolicyPresenters = [PSCustomObject]@{ - DesignatedPresenterRoleMode = "Enabled" - } - #> - $CsTeamsMeetingPolicyPresenters = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property DesignatedPresenterRoleMode - return $CsTeamsMeetingPolicyPresenters - } - '8.5.7' { - # Test-ExternalNoControl.ps1 - # 8.5.7 (L1) Ensure external participants can't give or request control - # - # Validate test for a pass: - # - Confirm that the automated test results align with the manual audit steps outlined in the CIS benchmark. - # - Specific conditions to check: - # - Condition A: Ensure the `AllowExternalParticipantGiveRequestControl` setting in Teams is set to `False`. - # - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command. - # - Condition C: Verification using the UI confirms that external participants are unable to give or request control. - # - # Validate test for a fail: - # - Confirm that the failure conditions in the automated test are consistent with the manual audit results. - # - Specific conditions to check: - # - Condition A: The `AllowExternalParticipantGiveRequestControl` setting in Teams is not set to `False`. - # - Condition B: The setting is verified through the Microsoft Teams admin center or via PowerShell command. - # - Condition C: Verification using the UI indicates that external participants can give or request control. - # Retrieve Teams meeting policy for external participant control - # $CsTeamsMeetingPolicyControl Mock Object - <# - $CsTeamsMeetingPolicyControl = [PSCustomObject]@{ - AllowExternalParticipantGiveRequestControl = $true - } - #> - $CsTeamsMeetingPolicyControl = Get-CsTeamsMeetingPolicy -Identity Global | Select-Object -Property AllowExternalParticipantGiveRequestControl - return $CsTeamsMeetingPolicyControl - } - '8.6.1' { - # Test-ReportSecurityInTeams.ps1 - # 8.6.1 (L1) Ensure users can report security concerns in Teams - # Retrieve the necessary settings for Teams and Exchange Online - # Condition A: Ensure the 'Report a security concern' setting in the Teams admin center is set to 'On'. - # $CsTeamsMessagingPolicy Mock Object - <# - $CsTeamsMessagingPolicy = [PSCustomObject]@{ - AllowSecurityEndUserReporting = $true - } - #> - $CsTeamsMessagingPolicy = Get-CsTeamsMessagingPolicy -Identity Global | Select-Object -Property AllowSecurityEndUserReporting - return $CsTeamsMessagingPolicy - } - default { throw "No match found for test: $Rec" } + } + catch { + throw "Get-CISMSTeamsOutput: `n$_" } } end { diff --git a/source/Private/Get-CISMgOutput.ps1 b/source/Private/Get-CISMgOutput.ps1 index 8c6bf99..7413e35 100644 --- a/source/Private/Get-CISMgOutput.ps1 +++ b/source/Private/Get-CISMgOutput.ps1 @@ -38,76 +38,86 @@ function Get-CISMgOutput { #> } process { - Write-Verbose "Get-CISMgOutput: Retuning data for Rec: $Rec" - switch ($rec) { - '1.1.1' { - # 1.1.1 - # Test-AdministrativeAccountCompliance - $AdminRoleAssignmentsAndUsers = Get-AdminRoleUserAndAssignment - return $AdminRoleAssignmentsAndUsers - } - '1.1.3' { - # Test-GlobalAdminsCount - # Step: Retrieve global admin role - $globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'" - # Step: Retrieve global admin members - $globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id - return $globalAdmins - } - '1.2.1' { - # Test-ManagedApprovedPublicGroups - $allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility - return $allGroups - } - '1.3.1' { - # Test-PasswordNeverExpirePolicy.ps1 - $domains = if ($DomainName) { - Get-MgDomain -DomainId $DomainName + try { + Write-Verbose "Get-CISMgOutput: Retuning data for Rec: $Rec" + switch ($rec) { + '1.1.1' { + # 1.1.1 + # Test-AdministrativeAccountCompliance + $AdminRoleAssignmentsAndUsers = Get-AdminRoleUserAndAssignment + return $AdminRoleAssignmentsAndUsers } - else { - Get-MgDomain + '1.1.3' { + # Test-GlobalAdminsCount + # Step: Retrieve global admin role + $globalAdminRole = Get-MgDirectoryRole -Filter "RoleTemplateId eq '62e90394-69f5-4237-9190-012177145e10'" + # Step: Retrieve global admin members + $globalAdmins = Get-MgDirectoryRoleMember -DirectoryRoleId $globalAdminRole.Id + return $globalAdmins } - return $domains - } - '5.1.2.3' { - # Test-RestrictTenantCreation - # Retrieve the tenant creation policy - $tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants - return $tenantCreationPolicy - } - '5.1.8.1' { - # Test-PasswordHashSync - # Retrieve password hash sync status (Condition A and C) - $passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled - return $passwordHashSync - } - '6.1.2' { - # Test-MailboxAuditingE3 - $tenantSkus = Get-MgSubscribedSku -All - $e3SkuPartNumber = "SPE_E3" - $founde3Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e3SkuPartNumber } - if ($founde3Sku.Count -ne 0) { - $allE3Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde3Sku.SkuId) )" -All - return $allE3Users + '1.2.1' { + # Test-ManagedApprovedPublicGroups + $allGroups = Get-MgGroup -All | Where-Object { $_.Visibility -eq "Public" } | Select-Object DisplayName, Visibility + return $allGroups } - else { - return $null + '1.2.2' { + # Test-BlockSharedMailboxSignIn.ps1 + $users = Get-MgUser + return $users } + '1.3.1' { + # Test-PasswordNeverExpirePolicy.ps1 + $domains = if ($DomainName) { + Get-MgDomain -DomainId $DomainName + } + else { + Get-MgDomain + } + return $domains + } + '5.1.2.3' { + # Test-RestrictTenantCreation + # Retrieve the tenant creation policy + $tenantCreationPolicy = (Get-MgPolicyAuthorizationPolicy).DefaultUserRolePermissions | Select-Object AllowedToCreateTenants + return $tenantCreationPolicy + } + '5.1.8.1' { + # Test-PasswordHashSync + # Retrieve password hash sync status (Condition A and C) + $passwordHashSync = Get-MgOrganization | Select-Object -ExpandProperty OnPremisesSyncEnabled + return $passwordHashSync + } + '6.1.2' { + # Test-MailboxAuditingE3 + $tenantSKUs = Get-MgSubscribedSku -All + $e3SkuPartNumber = "SPE_E3" + $foundE3Sku = $tenantSKUs | Where-Object { $_.SkuPartNumber -eq $e3SkuPartNumber } + if ($foundE3Sku.Count -ne 0) { + $allE3Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($foundE3Sku.SkuId) )" -All + return $allE3Users + } + else { + return $null + } + } + '6.1.3' { + # Test-MailboxAuditingE5 + $tenantSKUs = Get-MgSubscribedSku -All + $e5SkuPartNumber = "SPE_E5" + $foundE5Sku = $tenantSKUs | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber } + if ($foundE5Sku.Count -ne 0) { + $allE5Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($foundE5Sku.SkuId) )" -All + return $allE5Users + } + else { + return $null + } + } + default { throw "No match found for test: $Rec" } } - '6.1.3' { - # Test-MailboxAuditingE5 - $tenantSkus = Get-MgSubscribedSku -All - $e5SkuPartNumber = "SPE_E5" - $founde5Sku = $tenantSkus | Where-Object { $_.SkuPartNumber -eq $e5SkuPartNumber } - if ($founde5Sku.Count -ne 0) { - $allE5Users = Get-MgUser -Filter "assignedLicenses/any(x:x/skuId eq $($founde5Sku.SkuId) )" -All - return $allE5Users - } - else { - return $null - } - } - default { throw "No match found for test: $Rec" } + } + catch { + throw "Get-CISMgOutput: `n$_" } } end { diff --git a/source/Private/Get-CISSpoOutput.ps1 b/source/Private/Get-CISSpoOutput.ps1 index 99362cd..10a16ee 100644 --- a/source/Private/Get-CISSpoOutput.ps1 +++ b/source/Private/Get-CISSpoOutput.ps1 @@ -1,13 +1,3 @@ -<# - .SYNOPSIS - This is a sample Private function only visible within the module. - .DESCRIPTION - This sample function is not exported to the module and only return the data passed as parameter. - .EXAMPLE - $null = Get-CISSpoOutput -PrivateData 'NOTHING TO SEE HERE' - .PARAMETER PrivateData - The PrivateData parameter is what will be returned without transformation. -#> function Get-CISSpoOutput { [cmdletBinding()] param( @@ -15,186 +5,45 @@ function Get-CISSpoOutput { [String]$Rec ) begin { - # Begin Block # - <# - # Tests - 7.2.1 - 7.2.2 - 7.2.3 - 7.2.4 - 7.2.5 - 7.2.6 - 7.2.7 - 7.2.9 - 7.2.10 - 7.3.1 - 7.3.2 - 7.3.4 - - # Test number array - $testNumbers = @('7.2.1', '7.2.2', '7.2.3', '7.2.4', '7.2.5', '7.2.6', '7.2.7', '7.2.9', '7.2.10', '7.3.1', '7.3.2', '7.3.4') - #> - } - process { - Write-Verbose "Retuning data for Rec: $Rec" - switch ($Rec) { - '7.2.1' { - # Test-ModernAuthSharePoint.ps1 - # $SPOTenant Mock Object - <# - $SPOTenant = [PSCustomObject]@{ - LegacyAuthProtocolsEnabled = $true - } - #> - $SPOTenant = Get-SPOTenant | Select-Object -Property LegacyAuthProtocolsEnabled - return $SPOTenant - } - '7.2.2' { - # Test-SharePointAADB2B.ps1 - # 7.2.2 (L1) Ensure SharePoint and OneDrive integration with Azure AD B2B is enabled - # $SPOTenantAzureADB2B Mock Object - <# - $SPOTenantAzureADB2B = [PSCustomObject]@{ - EnableAzureADB2BIntegration = $false - } - #> - $SPOTenantAzureADB2B = Get-SPOTenant | Select-Object EnableAzureADB2BIntegration - return $SPOTenantAzureADB2B - } - '7.2.3' { - # Test-RestrictExternalSharing.ps1 - # 7.2.3 (L1) Ensure external content sharing is restricted - # Retrieve the SharingCapability setting for the SharePoint tenant - # $SPOTenantSharingCapability Mock Object - <# - $SPOTenantSharingCapability = [PSCustomObject]@{ - SharingCapability = "ExternalUserAndGuestSharing" - } - #> - $SPOTenantSharingCapability = Get-SPOTenant | Select-Object SharingCapability - return $SPOTenantSharingCapability - } - '7.2.4' { - # Test-OneDriveContentRestrictions.ps1 - # 7.2.4 (L2) Ensure OneDrive content sharing is restricted - # $SPOTenant Mock Object - <# - $SPOTenant = [PSCustomObject]@{ - OneDriveSharingCapability = "ExternalUserAndGuestSharing" - } - #> - $SPOTenant = Get-SPOTenant | Select-Object OneDriveSharingCapability - return $SPOTenant - } - '7.2.5' { - # Test-SharePointGuestsItemSharing.ps1 - # 7.2.5 (L2) Ensure that SharePoint guest users cannot share items they don't own - # $SPOTenant Mock Object - <# - $SPOTenant = [PSCustomObject]@{ - PreventExternalUsersFromResharing = $false - } - #> - $SPOTenant = Get-SPOTenant | Select-Object PreventExternalUsersFromResharing - return $SPOTenant - } - '7.2.6' { - # Test-SharePointExternalSharingDomains.ps1 - # 7.2.6 (L2) Ensure SharePoint external sharing is managed through domain whitelist/blacklists - # Add Authorized Domains? - # $SPOTenant Mock Object - <# - $SPOTenant = [PSCustomObject]@{ - SharingDomainRestrictionMode = "AllowList" - SharingAllowedDomainList = "domain1.com", "domain2.com" - } - #> - $SPOTenant = Get-SPOTenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList - return $SPOTenant - } - '7.2.7' { - # Test-LinkSharingRestrictions.ps1 - # Retrieve link sharing configuration for SharePoint and OneDrive - # $SPOTenantLinkSharing Mock Object - <# - $$SPOTenantLinkSharing = [PSCustomObject]@{ - DefaultSharingLinkType = "Direct" - } - #> - $SPOTenantLinkSharing = Get-SPOTenant | Select-Object DefaultSharingLinkType - return $SPOTenantLinkSharing - } - '7.2.9' { - # Test-GuestAccessExpiration.ps1 - # Retrieve SharePoint tenant settings related to guest access expiration - # $SPOTenantGuestAccess Mock Object - <# - $SPOTenantGuestAccess = [PSCustomObject]@{ - ExternalUserExpirationRequired = "$false" - ExternalUserExpireInDays = "60" - } - #> - $SPOTenantGuestAccess = Get-SPOTenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays - return $SPOTenantGuestAccess - } - '7.2.10' { - # Test-ReauthWithCode.ps1 - # 7.2.10 (L1) Ensure reauthentication with verification code is restricted - # Retrieve reauthentication settings for SharePoint Online - # $SPOTenantReauthentication Mock Object - <# - $SPOTenantReauthentication = [PSCustomObject]@{ - EmailAttestationRequired = "$false" - EmailAttestationReAuthDays = "30" - } - #> - $SPOTenantReauthentication = Get-SPOTenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays - return $SPOTenantReauthentication - } - '7.3.1' { - # Test-DisallowInfectedFilesDownload.ps1 - # Retrieve the SharePoint tenant configuration - # $SPOTenantDisallowInfectedFileDownload Mock Object - <# - $SPOTenantDisallowInfectedFileDownload = [PSCustomObject]@{ - DisallowInfectedFileDownload = $false - } - #> - $SPOTenantDisallowInfectedFileDownload = Get-SPOTenant | Select-Object DisallowInfectedFileDownload - return $SPOTenantDisallowInfectedFileDownload - } - '7.3.2' { - # Test-OneDriveSyncRestrictions.ps1 - # Retrieve OneDrive sync client restriction settings - # Add isHybrid paramter? - # $SPOTenantSyncClientRestriction Mock Object - <# - $SPOTenantSyncClientRestriction = [PSCustomObject]@{ - TenantRestrictionEnabled = $true - AllowedDomainList = "786548DD-877B-4760-A749-6B1EFBC1190A", "877564FF-877B-4760-A749-6B1EFBC1190A" - } - #> - $SPOTenantSyncClientRestriction = Get-SPOTenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList - return $SPOTenantSyncClientRestriction - } - '7.3.4' { - # Test-RestrictCustomScripts.ps1 - # Retrieve all site collections and select necessary properties - # $SPOSitesCustomScript Mock Object - <# - $SPOSitesCustomScript = [PSCustomObject]@{ - Title = "Site Collection 1" - Url = "https://contoso.sharepoint.com/sites/site1" - DenyAddAndCustomizePages = "Enabled" - } - #> - $SPOSitesCustomScript = Get-SPOSite -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages - return $SPOSitesCustomScript - } - default { throw "No match found for test: $Rec" } + if (($script:PnpAuth)) { + $UsePnP = $true + } + # Determine the prefix based on the switch + $prefix = if ($UsePnP) { "PnP" } else { "SPO" } + # Define a hashtable to map the function calls + $commandMap = @{ + '7.2.1' = "Get-${prefix}Tenant | Select-Object -Property LegacyAuthProtocolsEnabled" + '7.2.2' = "Get-${prefix}Tenant | Select-Object EnableAzureADB2BIntegration" + '7.2.3' = "Get-${prefix}Tenant | Select-Object SharingCapability" + '7.2.4' = "Get-${prefix}Tenant | Select-Object OneDriveSharingCapability" + '7.2.5' = "Get-${prefix}Tenant | Select-Object PreventExternalUsersFromResharing" + '7.2.6' = "Get-${prefix}Tenant | Select-Object SharingDomainRestrictionMode, SharingAllowedDomainList" + '7.2.7' = "Get-${prefix}Tenant | Select-Object DefaultSharingLinkType" + '7.2.9' = "Get-${prefix}Tenant | Select-Object ExternalUserExpirationRequired, ExternalUserExpireInDays" + '7.2.10' = "Get-${prefix}Tenant | Select-Object EmailAttestationRequired, EmailAttestationReAuthDays" + '7.3.1' = "Get-${prefix}Tenant | Select-Object DisallowInfectedFileDownload" + '7.3.2' = "Get-${prefix}TenantSyncClientRestriction | Select-Object TenantRestrictionEnabled, AllowedDomainList" + '7.3.4' = if ($prefix -eq "SPO") {"Get-${prefix}Site -Limit All | Select-Object Title, Url, DenyAddAndCustomizePages"} else {"Get-${Prefix}TenantSite | Select-Object Title, Url, DenyAddAndCustomizePages"} } } - end { - Write-Verbose "Retuning data for Rec: $Rec" + process { + try { + Write-Verbose "Returning data for Rec: $Rec" + if ($commandMap.ContainsKey($Rec)) { + $command = $commandMap[$Rec] + $result = Invoke-Expression $command + return $result + } + else { + throw "No match found for test: $Rec" + } + } + catch { + throw "Get-CISSpoOutput: `n$_" + } + } -} # end function Get-CISMSTeamsOutput \ No newline at end of file + end { + Write-Verbose "Finished processing for Rec: $Rec" + } +} diff --git a/source/Private/Get-RequiredModule.ps1 b/source/Private/Get-RequiredModule.ps1 index ac8f3fb..3646801 100644 --- a/source/Private/Get-RequiredModule.ps1 +++ b/source/Private/Get-RequiredModule.ps1 @@ -4,20 +4,27 @@ function Get-RequiredModule { param ( [Parameter(Mandatory = $true, ParameterSetName = 'AuditFunction')] [switch]$AuditFunction, - [Parameter(Mandatory = $true, ParameterSetName = 'SyncFunction')] [switch]$SyncFunction ) - switch ($PSCmdlet.ParameterSetName) { 'AuditFunction' { - return @( - @{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() }, - @{ ModuleName = "AzureAD"; RequiredVersion = "2.0.2.182"; SubModules = @() }, - @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") }, - @{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() }, - @{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() } - ) + if (($script:PnpAuth)) { + return @( + @{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() }, + @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") }, + @{ ModuleName = "PnP.PowerShell"; RequiredVersion = "2.5.0"; SubModules = @() }, + @{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() } + ) + } + else { + return @( + @{ ModuleName = "ExchangeOnlineManagement"; RequiredVersion = "3.3.0"; SubModules = @() }, + @{ ModuleName = "Microsoft.Graph"; RequiredVersion = "2.4.0"; SubModules = @("DeviceManagement", "Users", "Identity.DirectoryManagement", "Identity.SignIns") }, + @{ ModuleName = "Microsoft.Online.SharePoint.PowerShell"; RequiredVersion = "16.0.24009.12000"; SubModules = @() }, + @{ ModuleName = "MicrosoftTeams"; RequiredVersion = "5.5.0"; SubModules = @() } + ) + } } 'SyncFunction' { return @( diff --git a/source/Private/Get-UniqueConnection.ps1 b/source/Private/Get-UniqueConnection.ps1 index a05f282..dbd2888 100644 --- a/source/Private/Get-UniqueConnection.ps1 +++ b/source/Private/Get-UniqueConnection.ps1 @@ -8,10 +8,7 @@ function Get-UniqueConnection { $uniqueConnections = @() - if ($Connections -contains "AzureAD" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") { - $uniqueConnections += "AzureAD" - } - if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") { + if ($Connections -contains "Microsoft Graph" -or $Connections -contains "AzureAD | EXO | Microsoft Graph" -or $Connections -contains "EXO | Microsoft Graph") { $uniqueConnections += "Microsoft Graph" } if ($Connections -contains "EXO" -or $Connections -contains "AzureAD | EXO" -or $Connections -contains "Microsoft Teams | EXO" -or $Connections -contains "AzureAD | EXO | Microsoft Graph") { diff --git a/source/Private/Measure-AuditResult.ps1 b/source/Private/Measure-AuditResult.ps1 index 4a8b43b..5d38ec4 100644 --- a/source/Private/Measure-AuditResult.ps1 +++ b/source/Private/Measure-AuditResult.ps1 @@ -18,8 +18,8 @@ function Measure-AuditResult { $passPercentage = if ($totalTests -eq 0) { 0 } else { [math]::Round(($passedTests / $totalTests) * 100, 2) } # Display the pass percentage to the user - Write-Verbose "Audit completed. $passedTests out of $totalTests tests passed." - Write-Verbose "Your passing percentage is $passPercentage%." + Write-Information "Audit completed. $passedTests out of $totalTests tests passed." + Write-Information "Your passing percentage is $passPercentage%." # Display details of failed tests if ($FailedTests.Count -gt 0) { diff --git a/source/Public/Invoke-M365SecurityAudit.ps1 b/source/Public/Invoke-M365SecurityAudit.ps1 index baeca4e..8adf6b1 100644 --- a/source/Public/Invoke-M365SecurityAudit.ps1 +++ b/source/Public/Invoke-M365SecurityAudit.ps1 @@ -37,6 +37,8 @@ If specified, the cmdlet will not check for the presence of required modules. .PARAMETER DoNotConfirmConnections If specified, the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them. + .PARAMETER AuthParams + Specifies an authentication object containing parameters for application-based authentication. If provided, this will be used for connecting to services. .EXAMPLE PS> Invoke-M365SecurityAudit @@ -203,13 +205,18 @@ function Invoke-M365SecurityAudit { [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not check for the presence of required modules.")] [switch]$NoModuleCheck, [Parameter(Mandatory = $false, HelpMessage = "Specifies that the cmdlet will not prompt for confirmation before proceeding with established connections and will disconnect from all of them.")] - [switch]$DoNotConfirmConnections + [switch]$DoNotConfirmConnections, + [Parameter(Mandatory = $false, HelpMessage = "Specifies an authentication object containing parameters for application-based authentication.")] + [CISAuthenticationParameters]$AuthParams ) Begin { if ($script:MaximumFunctionCount -lt 8192) { Write-Verbose "Setting the `$script:MaximumFunctionCount to 8192 for the test run." $script:MaximumFunctionCount = 8192 } + if ($AuthParams) { + $script:PnpAuth = $true + } # Ensure required modules are installed $requiredModules = Get-RequiredModule -AuditFunction # Format the required modules list @@ -267,7 +274,7 @@ function Invoke-M365SecurityAudit { $actualUniqueConnections = Get-UniqueConnection -Connections $requiredConnections if (!($DoNotConnect) -and $PSCmdlet.ShouldProcess("Establish connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')", "Connect")) { Write-Information "Establishing connections to Microsoft 365 services: $($actualUniqueConnections -join ', ')" - Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections -SkipConfirmation:$DoNotConfirmConnections + Connect-M365Suite -TenantAdminUrl $TenantAdminUrl -RequiredConnections $requiredConnections -SkipConfirmation:$DoNotConfirmConnections -AuthParams $AuthParams } } catch { @@ -331,4 +338,4 @@ function Invoke-M365SecurityAudit { End { } -} \ No newline at end of file +} diff --git a/source/Public/New-CISAuthenticationParameters.ps1 b/source/Public/New-CISAuthenticationParameters.ps1 new file mode 100644 index 0000000..dc21728 --- /dev/null +++ b/source/Public/New-CISAuthenticationParameters.ps1 @@ -0,0 +1,61 @@ +<# + .SYNOPSIS + Creates a new CISAuthenticationParameters object for Microsoft 365 authentication. + .DESCRIPTION + The New-CISAuthenticationParameters function constructs a new CISAuthenticationParameters object + containing the necessary credentials and URLs for authenticating to various Microsoft 365 services. + It validates input parameters to ensure they conform to expected formats and length requirements. + .PARAMETER ClientCertThumbPrint + The thumbprint of the client certificate used for authentication. It must be a 40-character hexadecimal string. + This certificate is used to authenticate the application in Azure AD. + .PARAMETER ClientId + The Client ID (Application ID) of the Azure AD application. It must be a valid GUID format. + .PARAMETER TenantId + The Tenant ID of the Azure AD directory. It must be a valid GUID format representing your Microsoft 365 tenant. + .PARAMETER OnMicrosoftUrl + The URL of your onmicrosoft.com domain. It should be in the format 'example.onmicrosoft.com'. + .PARAMETER SpAdminUrl + The SharePoint admin URL, which should end with '-admin.sharepoint.com'. This URL is used for connecting to SharePoint Online. + .INPUTS + None. You cannot pipe objects to this function. + .OUTPUTS + CISAuthenticationParameters + The function returns an instance of the CISAuthenticationParameters class containing the authentication details. + .EXAMPLE + PS> $authParams = New-CISAuthenticationParameters -ClientCertThumbPrint "ABCDEF1234567890ABCDEF1234567890ABCDEF12" ` + -ClientId "6aa0efb0-31c8-4215-9071-662ba6b2443c" ` + -TenantId "54407279-440c-4691-90af-ed56de3ef801" ` + -OnMicrosoftUrl "yourcompany.onmicrosoft.com" ` + -SpAdminUrl "https://yourcompany-admin.sharepoint.com" + Creates a new CISAuthenticationParameters object with the specified credentials and URLs, validating each parameter's format and length. +#> +function New-CISAuthenticationParameters { + [CmdletBinding()] + [OutputType([CISAuthenticationParameters])] + param( + [Parameter(Mandatory = $true, HelpMessage = "The 40-character hexadecimal thumbprint of the client certificate.")] + [ValidatePattern("^[0-9a-fA-F]{40}$")] # Regex for a valid thumbprint format + [ValidateLength(40, 40)] # Enforce exact length + [string]$ClientCertThumbPrint, + [Parameter(Mandatory = $true, HelpMessage = "The Client ID (GUID format) of the Azure AD application.")] + [ValidatePattern("^[0-9a-fA-F\-]{36}$")] # Regex for a valid GUID + [string]$ClientId, + [Parameter(Mandatory = $true, HelpMessage = "The Tenant ID (GUID format) of the Azure AD directory.")] + [ValidatePattern("^[0-9a-fA-F\-]{36}$")] # Regex for a valid GUID + [string]$TenantId, + [Parameter(Mandatory = $true, HelpMessage = "The onmicrosoft.com domain URL (e.g., 'example.onmicrosoft.com').")] + [ValidatePattern("^[a-zA-Z0-9]+\.onmicrosoft\.com$")] # Regex for a valid onmicrosoft.com URL + [string]$OnMicrosoftUrl, + [Parameter(Mandatory = $true, HelpMessage = "The SharePoint admin URL ending with '-admin.sharepoint.com'.")] + [ValidatePattern("^https:\/\/[a-zA-Z0-9\-]+\-admin\.sharepoint\.com$")] # Regex for a valid SharePoint admin URL + [string]$SpAdminUrl + ) + # Create and return the authentication parameters object + return [CISAuthenticationParameters]::new( + $ClientCertThumbPrint, + $ClientId, + $TenantId, + $OnMicrosoftUrl, + $SpAdminUrl + ) +} \ No newline at end of file diff --git a/source/helper/TestDefinitions.csv b/source/helper/TestDefinitions.csv index e12213b..0566724 100644 --- a/source/helper/TestDefinitions.csv +++ b/source/helper/TestDefinitions.csv @@ -2,7 +2,7 @@ 1,Test-AdministrativeAccountCompliance.ps1,1.1.1,Ensure Administrative accounts are separate and cloud-only,E3,L1,5.4,Restrict Administrator Privileges to Dedicated Administrator Accounts,TRUE,TRUE,TRUE,FALSE,Microsoft Graph 2,Test-GlobalAdminsCount.ps1,1.1.3,Ensure that between two and four global admins are designated,E3,L1,5.1,Establish and Maintain an Inventory of Accounts,TRUE,TRUE,TRUE,TRUE,Microsoft Graph 3,Test-ManagedApprovedPublicGroups.ps1,1.2.1,Ensure that only organizationally managed/approved public groups exist,E3,L2,3.3,Configure Data Access Control Lists,TRUE,TRUE,TRUE,TRUE,Microsoft Graph -4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,AzureAD | EXO +4,Test-BlockSharedMailboxSignIn.ps1,1.2.2,Ensure sign-in to shared mailboxes is blocked,E3,L1,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO | Microsoft Graph 5,Test-PasswordNeverExpirePolicy.ps1,1.3.1,Ensure the 'Password expiration policy' is set to 'Set passwords to never expire',E3,L1,5.2,Use Unique Passwords,TRUE,TRUE,TRUE,TRUE,Microsoft Graph 6,Test-ExternalSharingCalendars.ps1,1.3.3,Ensure 'External sharing' of calendars is not available,E3,L2,4.8,Uninstall or Disable Unnecessary Services on Enterprise Assets and Software,FALSE,TRUE,TRUE,TRUE,EXO 7,Test-CustomerLockbox.ps1,1.3.6,Ensure the customer lockbox feature is enabled,E5,L2,0,Explicitly Not Mapped,FALSE,FALSE,FALSE,TRUE,EXO diff --git a/source/tests/Test-BlockSharedMailboxSignIn.ps1 b/source/tests/Test-BlockSharedMailboxSignIn.ps1 index d0edeaf..69928ea 100644 --- a/source/tests/Test-BlockSharedMailboxSignIn.ps1 +++ b/source/tests/Test-BlockSharedMailboxSignIn.ps1 @@ -58,9 +58,9 @@ function Test-BlockSharedMailboxSignIn { } ) #> - $users = Get-CISAadOutput -Rec $recnum + $users = Get-CISMgOutput -Rec $recnum # Step: Retrieve details of shared mailboxes from Azure AD (Condition B: Pass/Fail) - $sharedMailboxDetails = $users | Where-Object {$_.objectid -in $objectids} + $sharedMailboxDetails = $users | Where-Object {$_.id -in $objectids} # Step: Identify enabled mailboxes (Condition B: Pass/Fail) $enabledMailboxes = $sharedMailboxDetails | Where-Object { $_.AccountEnabled } | ForEach-Object { $_.DisplayName } $allBlocked = $enabledMailboxes.Count -eq 0