param( [Parameter(Mandatory=$true)] [string]$HaloUri, # Exchange params â mandatory in Standalone sets only [string]$ExchangeCertificatePath, [string]$ExchangeAppId, [string]$ExchangeOrganization, # Optional local/interactive login via UPN instead of cert [string]$UserPrincipalName, # Standalone switch â present in Halo, assumed Standalone if absent [switch]$Halo, [Parameter(Mandatory=$true, ParameterSetName='None')] [switch]$NoAuth, [Parameter(Mandatory=$true, ParameterSetName='Bearer')] [string]$HaloToken, [Parameter(Mandatory=$true, ParameterSetName='Basic')] [System.Management.Automation.PSCredential]$HaloCredential, [Parameter(Mandatory=$false)] [string]$OutlookMonitorsPath = "/api/storage-monitor/Outlook/v1/monitors" ) # If not running in Halo, ensure Exchange connection is established for distribution group management if (-not $Halo) { Write-Host("Starting Glasswall Outlook Banner synchronization...") if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) { Write-Warning "EXO module not found. Installing..." Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser -Force } if (-not (Get-Module -Name ExchangeOnlineManagement)) { Write-Verbose "Importing EXO module..." Import-Module ExchangeOnlineManagement } try { Get-OrganizationConfig -ErrorAction Stop | Out-Null Write-Host "Connected to Exchange Online" -ForegroundColor Green } catch { Write-Verbose "Not connected to Exchange Online. Initiating connection..." if ($UserPrincipalName) { Write-Host "Opening browser login for $UserPrincipalName..." Connect-ExchangeOnline -UserPrincipalName $UserPrincipalName ` -ShowBanner:$false } else { Write-Warning "No UserPrincipalName provided, falling back to certificate authentication. Please ensure certificate parameters are provided..." $parametersMissing = $false if (-not $ExchangeCertificatePath) { Write-Error "-ExchangeCertificatePath is required"; $parametersMissing = $true } if (-not $ExchangeAppId) { Write-Error "-ExchangeAppId is required"; $parametersMissing = $true } if (-not $ExchangeOrganization) { Write-Error "-ExchangeOrganization is required"; $parametersMissing = $true } if ($parametersMissing) { Write-Error "One or more Exchange parameters are missing - see previous errors. Alternatively, please provide a valid email login for UserPrincipalName" exit 1 } Write-Host "Connecting to Exchange Online via certificate authentication..." Connect-ExchangeOnline -AppId $ExchangeAppId ` -CertificateFilePath $ExchangeCertificatePath ` -Organization $ExchangeOrganization ` -ShowBanner:$false } Write-Host "Connected to Exchange Online" -ForegroundColor Green } } $HaloUri = $HaloUri.TrimEnd('/') # Set up parameters for API call, including authentication if needed $page = 1 $pageSize = 10 $headers = @{ "Accept" = "application/json" } $credentialParam = @{} switch ($PSCmdlet.ParameterSetName) { "None" { } "Bearer" { $headers["authorization"] = "Bearer $HaloToken" } "Basic" { $credentialParam["Credential"] = $HaloCredential } } # Retrieve all monitors from Halo, handling pagination Write-Verbose "Info: Retrieving monitor data from Halo API..." $allMonitors = [System.Collections.Generic.List[object]]::new() try { do { $HaloLocation = "$($OutlookMonitorsPath)?pageSize=$pageSize&pageIndex=$page" $response = Invoke-RestMethod -Uri "$HaloUri$HaloLocation" ` -Method Get ` -Headers $headers ` -ErrorAction Stop ` @credentialParam # Optional parameter splatting for authentication if ($null -ne $response.monitors) { $allMonitors.AddRange($response.monitors) } else { Write-Error "Unexpected null response from Halo API. Please check the configured URI is correct. Aborting synchronization..." exit 1 } $page++ } while ($response.pageLinks.pages -and $page -le $response.pageLinks.pages) } catch { Write-Error "Something went wrong while retrieving data from the Halo API. Aborting synchronization..." exit 1 } # Pull emails from active monitors $activeMonitorEmails = @($allMonitors | Where-Object { $_.status -notin @("Stopped", "Deleted") } | ForEach-Object { $_.resource.email } | Where-Object { $_ }) $distroGroupName = "GWStorageMonitoringUsers" $distroEmails = (Get-DistributionGroupMember -Identity $distroGroupName).PrimarySmtpAddress $emailsToAdd = $activeMonitorEmails | Where-Object { $_ -notin $distroEmails } $emailsToRemove = $distroEmails | Where-Object { $_ -notin $activeMonitorEmails } if (-not $emailsToAdd -and -not $emailsToRemove) { Write-Information "Info: Distribution group is already synchronized. No changes made" exit 0 } # Add new emails to distribution group try { foreach ($user in $emailsToAdd) { Add-DistributionGroupMember -Identity $distroGroupName -Member $user Write-Verbose "Info: Verifying user was added to Distribution Group" $members = Get-DistributionGroupMember -Identity $distroGroupName $memberExists = $members | Where-Object { $_.PrimarySmtpAddress -eq $user } if ($memberExists) { Write-Verbose "Success: User was added to Distribution Group for monitoring" } else { Write-Warning "Unable to add user to the Distribution Group" } } } catch { Write-Warning "Something went wrong when adding users to the Distribution Group" } # Remove old emails from distribution group try { foreach ($user in $emailsToRemove) { Remove-DistributionGroupMember -Identity $distroGroupName -Member $user -Confirm:$false -ErrorAction SilentlyContinue Write-Verbose "Info: Verifying user was removed from Distribution Group" $members = Get-DistributionGroupMember -Identity $distroGroupName $memberExists = $members | Where-Object { $_.primarySmtpAddress -eq $user } if (-Not $memberExists) { Write-Verbose "Success: User was removed Distribution Group and will no longer receive monitoring banners" } else { Write-Warning "Unable to remove user from the Distribution Group" } } } catch { Write-Warning "Something went wrong when removing users from the Distribution Group" } if (-not $Halo) { Write-Host "Glasswall Outlook Banner synchronization complete" -ForegroundColor Green }