Merge branch 'main' into 365-4.0.0-Update

This commit is contained in:
Doug Rios
2025-03-23 15:34:35 -05:00
committed by GitHub
4 changed files with 129 additions and 105 deletions

View File

@@ -4,6 +4,20 @@ The format is based on and uses the types of changes according to [Keep a Change
## [Unreleased] ## [Unreleased]
### Fixed
- Get-SPOSite command to return all but voided output for no code runs (Ex: PowerAutomate)
## [0.1.27] - 2025-01-13
### Added
- Added additional error handling to connect function to identify problematic steps when they occur.
- Added new method of verifying spo tenant for Connect-SPOService branch of connect function.
- Added method to avoid "assembly already loaded" error in PNP Powershell function on first run, subsequent runs in the same session will still throw the error.
## [0.1.26] - 2024-08-04
### Added ### Added
- Link to App Authentication documentation in `New-M365SecurityAuditAuthObject` help file. - Link to App Authentication documentation in `New-M365SecurityAuditAuthObject` help file.
@@ -41,7 +55,6 @@ The format is based on and uses the types of changes according to [Keep a Change
- Fixed test 1.3.1 as notification window for password expiration is no longer required. - Fixed test 1.3.1 as notification window for password expiration is no longer required.
## [0.1.24] - 2024-07-07 ## [0.1.24] - 2024-07-07
### Added ### Added

View File

@@ -5,10 +5,10 @@ Import-Module .\output\module\M365FoundationsCISReport\*\*.psd1
<# <#
$ver = "v0.1.26" $ver = "v0.1.28"
git checkout main git checkout main
git pull origin main git pull origin main
git tag -a $ver -m "Release version $ver refactor Update" git tag -a $ver -m "Release version $ver bugfix Update"
git push origin $ver git push origin $ver
"Fix: PR #37" "Fix: PR #37"
git push origin $ver git push origin $ver

View File

@@ -46,4 +46,5 @@ function Assert-ModuleAvailability {
throw "Assert-ModuleAvailability:`n$_" throw "Assert-ModuleAvailability:`n$_"
} }
} }
} }

View File

@@ -2,122 +2,131 @@ function Connect-M365Suite {
[OutputType([void])] [OutputType([void])]
[CmdletBinding()] [CmdletBinding()]
param ( param (
[Parameter( [Parameter(Mandatory = $false)]
Mandatory = $false
)]
[string]$TenantAdminUrl, [string]$TenantAdminUrl,
[Parameter(
Mandatory = $false [Parameter(Mandatory = $false)]
)] [CISAuthenticationParameters]$AuthParams,
[CISAuthenticationParameters]$AuthParams, # Custom authentication parameters
[Parameter( [Parameter(Mandatory)]
Mandatory
)]
[string[]]$RequiredConnections, [string[]]$RequiredConnections,
[Parameter(
Mandatory = $false [Parameter(Mandatory = $false)]
)]
[switch]$SkipConfirmation [switch]$SkipConfirmation
) )
if (!$SkipConfirmation) {
$VerbosePreference = "Continue" $VerbosePreference = if ($SkipConfirmation) { 'SilentlyContinue' } else { 'Continue' }
}
else {
$VerbosePreference = "SilentlyContinue"
}
$tenantInfo = @() $tenantInfo = @()
$connectedServices = @() $connectedServices = @()
try { try {
if ($RequiredConnections -contains "Microsoft Graph" -or $RequiredConnections -contains "EXO | Microsoft Graph") { if ($RequiredConnections -contains 'Microsoft Graph' -or $RequiredConnections -contains 'EXO | Microsoft Graph') {
Write-Verbose "Connecting to Microsoft Graph" try {
Write-Verbose 'Connecting to Microsoft Graph...'
if ($AuthParams) { if ($AuthParams) {
# Use application-based authentication
Connect-MgGraph -CertificateThumbprint $AuthParams.ClientCertThumbPrint -AppId $AuthParams.ClientId -TenantId $AuthParams.TenantId -NoWelcome | Out-Null Connect-MgGraph -CertificateThumbprint $AuthParams.ClientCertThumbPrint -AppId $AuthParams.ClientId -TenantId $AuthParams.TenantId -NoWelcome | Out-Null
} }
else { else {
# Use interactive authentication with scopes Connect-MgGraph -Scopes 'Directory.Read.All', 'Domain.Read.All', 'Policy.Read.All', 'Organization.Read.All' -NoWelcome | Out-Null
Connect-MgGraph -Scopes "Directory.Read.All", "Domain.Read.All", "Policy.Read.All", "Organization.Read.All" -NoWelcome | Out-Null
} }
$graphOrgDetails = Get-MgOrganization $graphOrgDetails = Get-MgOrganization
$tenantInfo += [PSCustomObject]@{ $tenantInfo += [PSCustomObject]@{
Service = "Microsoft Graph" Service = 'Microsoft Graph'
TenantName = $graphOrgDetails.DisplayName TenantName = $graphOrgDetails.DisplayName
TenantID = $graphOrgDetails.Id TenantID = $graphOrgDetails.Id
} }
$connectedServices += "Microsoft Graph" $connectedServices += 'Microsoft Graph'
Write-Verbose "Successfully connected to Microsoft Graph.`n" Write-Verbose 'Successfully connected to Microsoft Graph.'
} }
if ($RequiredConnections -contains "EXO" -or $RequiredConnections -contains "AzureAD | EXO" -or $RequiredConnections -contains "Microsoft Teams | EXO" -or $RequiredConnections -contains "EXO | Microsoft Graph") { catch {
Write-Verbose "Connecting to Exchange Online..." throw "Failed to connect to Microsoft Graph: $($_.Exception.Message)"
}
}
if ($RequiredConnections -contains 'EXO' -or $RequiredConnections -contains 'AzureAD | EXO' -or $RequiredConnections -contains 'Microsoft Teams | EXO' -or $RequiredConnections -contains 'EXO | Microsoft Graph') {
try {
Write-Verbose 'Connecting to Exchange Online...'
if ($AuthParams) { if ($AuthParams) {
# Use application-based authentication
Connect-ExchangeOnline -AppId $AuthParams.ClientId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -Organization $AuthParams.OnMicrosoftUrl -ShowBanner:$false | Out-Null Connect-ExchangeOnline -AppId $AuthParams.ClientId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -Organization $AuthParams.OnMicrosoftUrl -ShowBanner:$false | Out-Null
} }
else { else {
# Use interactive authentication
Connect-ExchangeOnline -ShowBanner:$false | Out-Null Connect-ExchangeOnline -ShowBanner:$false | Out-Null
} }
$exoTenant = (Get-OrganizationConfig).Identity $exoTenant = (Get-OrganizationConfig).Identity
$tenantInfo += [PSCustomObject]@{ $tenantInfo += [PSCustomObject]@{
Service = "Exchange Online" Service = 'Exchange Online'
TenantName = $exoTenant TenantName = $exoTenant
TenantID = "N/A" TenantID = 'N/A'
} }
$connectedServices += "EXO" $connectedServices += 'EXO'
Write-Verbose "Successfully connected to Exchange Online.`n" Write-Verbose 'Successfully connected to Exchange Online.'
} }
if ($RequiredConnections -contains "SPO") { catch {
Write-Verbose "Connecting to SharePoint Online..." throw "Failed to connect to Exchange Online: $($_.Exception.Message)"
}
}
if ($RequiredConnections -contains 'SPO') {
try {
Write-Verbose 'Connecting to SharePoint Online...'
if ($AuthParams) { if ($AuthParams) {
# Use application-based authentication
Connect-PnPOnline -Url $AuthParams.SpAdminUrl -ClientId $AuthParams.ClientId -Tenant $AuthParams.OnMicrosoftUrl -Thumbprint $AuthParams.ClientCertThumbPrint | Out-Null Connect-PnPOnline -Url $AuthParams.SpAdminUrl -ClientId $AuthParams.ClientId -Tenant $AuthParams.OnMicrosoftUrl -Thumbprint $AuthParams.ClientCertThumbPrint | Out-Null
} }
else { else {
# Use interactive authentication
Connect-SPOService -Url $TenantAdminUrl | Out-Null Connect-SPOService -Url $TenantAdminUrl | Out-Null
} }
# Assuming that Get-SPOCrossTenantHostUrl and Get-UrlLine are valid commands in your context $tenantName = if ($AuthParams) {
if ($AuthParams) { (Get-PnPSite).Url
$spoContext = Get-PnPSite
$tenantName = $spoContext.Url
} }
else { else {
$spoContext = Get-SPOCrossTenantHostUrl # Supress output from Get-SPOSite for powerautomate to avoid errors
$tenantName = Get-UrlLine -Output $spoContext [void]($sites = Get-SPOSite -Limit All)
# Get the URL from the first site collection
$url = $sites[0].Url
# Use regex to extract the base URL up to the .com portion
$baseUrl = [regex]::Match($url, 'https://[^/]+.com').Value
# Output the base URL
$baseUrl
} }
$tenantInfo += [PSCustomObject]@{ $tenantInfo += [PSCustomObject]@{
Service = "SharePoint Online" Service = 'SharePoint Online'
TenantName = $tenantName TenantName = $tenantName
} }
$connectedServices += "SPO" $connectedServices += 'SPO'
Write-Verbose "Successfully connected to SharePoint Online.`n" Write-Verbose 'Successfully connected to SharePoint Online.'
} }
if ($RequiredConnections -contains "Microsoft Teams" -or $RequiredConnections -contains "Microsoft Teams | EXO") { catch {
Write-Verbose "Connecting to Microsoft Teams..." throw "Failed to connect to SharePoint Online: $($_.Exception.Message)"
}
}
if ($RequiredConnections -contains 'Microsoft Teams' -or $RequiredConnections -contains 'Microsoft Teams | EXO') {
try {
Write-Verbose 'Connecting to Microsoft Teams...'
if ($AuthParams) { if ($AuthParams) {
# Use application-based authentication
Connect-MicrosoftTeams -TenantId $AuthParams.TenantId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -ApplicationId $AuthParams.ClientId | Out-Null Connect-MicrosoftTeams -TenantId $AuthParams.TenantId -CertificateThumbprint $AuthParams.ClientCertThumbPrint -ApplicationId $AuthParams.ClientId | Out-Null
} }
else { else {
# Use interactive authentication
Connect-MicrosoftTeams | Out-Null Connect-MicrosoftTeams | Out-Null
} }
$teamsTenantDetails = Get-CsTenant $teamsTenantDetails = Get-CsTenant
$tenantInfo += [PSCustomObject]@{ $tenantInfo += [PSCustomObject]@{
Service = "Microsoft Teams" Service = 'Microsoft Teams'
TenantName = $teamsTenantDetails.DisplayName TenantName = $teamsTenantDetails.DisplayName
TenantID = $teamsTenantDetails.TenantId TenantID = $teamsTenantDetails.TenantId
} }
$connectedServices += "Microsoft Teams" $connectedServices += 'Microsoft Teams'
Write-Verbose "Successfully connected to Microsoft Teams.`n" Write-Verbose 'Successfully connected to Microsoft Teams.'
} }
# Display tenant information and confirm with the user catch {
throw "Failed to connect to Microsoft Teams: $($_.Exception.Message)"
}
}
if (-not $SkipConfirmation) { if (-not $SkipConfirmation) {
Write-Verbose "Connected to the following tenants:" Write-Verbose 'Connected to the following tenants:'
foreach ($tenant in $tenantInfo) { foreach ($tenant in $tenantInfo) {
Write-Verbose "Service: $($tenant.Service)" Write-Verbose "Service: $($tenant.Service) | Tenant: $($tenant.TenantName)"
Write-Verbose "Tenant Context: $($tenant.TenantName)`n"
#Write-Verbose "Tenant ID: $($tenant.TenantID)"
} }
if ($script:PnpAuth) { if ($script:PnpAuth) {
Write-Warning "`n!!!!!!!!!!!!Important!!!!!!!!!!!!!!`nIf you use the auth object, you will need to kill the current session before subsequent runs`nas the PNP.Powershell module has conflicts with MgGraph!`nIf Invoke-M365SecurityAudit is invoked in a runspace, you can circumvent the issue!`n!!!!!!!!!!!!Important!!!!!!!!!!!!!!" Write-Warning "`n!!!!!!!!!!!!Important!!!!!!!!!!!!!!`nIf you use the auth object, you will need to kill the current session before subsequent runs`nas the PNP.Powershell module has conflicts with MgGraph!`nIf Invoke-M365SecurityAudit is invoked in a runspace, you can circumvent the issue!`n!!!!!!!!!!!!Important!!!!!!!!!!!!!!"
@@ -126,14 +135,15 @@ function Connect-M365Suite {
if ($confirmation -notLike 'Y') { if ($confirmation -notLike 'Y') {
Write-Verbose "Connection setup aborted by user." Write-Verbose "Connection setup aborted by user."
Disconnect-M365Suite -RequiredConnections $connectedServices Disconnect-M365Suite -RequiredConnections $connectedServices
throw "User aborted connection setup." throw 'User aborted connection setup.'
} }
} }
} }
catch { catch {
$CatchError = $_ $VerbosePreference = 'Continue'
$VerbosePreference = "Continue" throw "Connection failed: $($_.Exception.Message)"
throw $CatchError }
finally {
$VerbosePreference = 'Continue'
} }
$VerbosePreference = "Continue"
} }