Last active
August 20, 2025 11:53
-
-
Save jpawlowski/f6ec653d54ea88e144a80c4f9ff8b64f to your computer and use it in GitHub Desktop.
Revisions
-
jpawlowski revised this gist
Apr 28, 2025 . 1 changed file with 11 additions and 25 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -136,7 +136,7 @@ .NOTES Filename: New-Temporary-Access-Pass-for-Initial-MFA-Setup-V2.ps1 Author: Julian Pawlowski, Workoho GmbH <[email protected]> Version: 2.3.1 #> #Requires -Version 7.4 #Requires -Modules @{ ModuleName='Microsoft.Graph.Authentication'; ModuleVersion='2.0' } @@ -1007,11 +1007,11 @@ function Send-EmailNotification { } de = @{ TAPCreated = 'Befristeter Zugriffscode für den Onboarding-Prozess' UserBlocked = 'Erstellung des befristeten Zugriffscodes blockiert' TAPNotEnabled = 'Nutzung des befristeten Zugriffscodes nicht freigeschaltet' TAPExpiredWithOtherMethodsConfigured = 'Verlängerung des befristeten Zugriffscodes nicht möglich' TAPActiveWithOtherMethodsConfigured = 'Neuerstellung des befristeten Zugriffscodes nicht möglich' OtherMethodsConfigured = 'Erstellung des befristeten Zugriffscodes nicht möglich' } fr = @{ TAPCreated = "Code d’accès temporaire pour l’intégration" @@ -3917,6 +3917,11 @@ catch { throw "Failed to retrieve user information: $_" } if ($null -eq $userObj.manager -or -not $userObj.manager.id) { Write-Error 'User ID does not have a manager.' exit 1 } if ($SendEmailToManager) { if ([string]::IsNullOrEmpty($userObj.manager.mail)) { Write-Error "Cannot send email: Manager for $($userObj.displayName) does not have an email address." @@ -4010,25 +4015,6 @@ if ($null -ne $userObj.employeeHireDate) { } } # Get user security groups try { $userGroupsResponse = Invoke-ResilientRemoteCall { -
jpawlowski revised this gist
Apr 26, 2025 . 1 changed file with 6 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1428,7 +1428,7 @@ function Send-EmailNotification { <!-- Justification section --> <tr> <td> <p style="margin:10px 0;">The creation was aborted because {{ userGivenName }} has already completed the onboarding process and has other authentication methods configured.</p> <p>The previously issued Temporary Access Pass is no longer valid.</p> <p style="margin:10px 0;">Please ask {{ userGivenName }} to sign in using their existing authentication methods.</p> @@ -1469,7 +1469,7 @@ function Send-EmailNotification { <!-- Justification section --> <tr> <td> <p style="margin:10px 0;">The creation was aborted because {{ userGivenName }} has already completed the onboarding process and has other authentication methods configured.</p> <p>{{ userGivenName }} must use the existing Temporary Access Pass to sign in before being able to use the other authentication methods.</p> <p style="margin:10px 0;">If {{ userGivenName }} is able to sign in using the active Temporary Access Pass, they can remove it themselves at <a href="https://aka.ms/MySecurityInfo" style="color:#0066cc; text-decoration:underline; font-weight:500;">https://aka.ms/MySecurityInfo</a> to start using their other authentication methods.</p> @@ -1511,7 +1511,7 @@ function Send-EmailNotification { <!-- Justification section --> <tr> <td> <p style="margin:10px 0;">The creation was aborted because {{ userGivenName }} has already completed the onboarding process and already has authentication methods configured.</p> <p>Temporary Access Pass codes must no longer be issued via the self-service process and require handling by IT support.</p> <p style="margin:10px 0;">Please ask {{ userGivenName }} to sign in using their existing authentication methods.</p> @@ -3066,7 +3066,7 @@ A Temporary Access Pass code could not be created for the following person: {{ userDisplayName }} {{ userPrincipalName }} The creation was aborted because {{ userGivenName }} has already completed the onboarding process and has other authentication methods configured. The previously issued Temporary Access Pass is no longer valid. Please ask {{ userGivenName }} to sign in using their existing authentication methods. @@ -3082,7 +3082,7 @@ A Temporary Access Pass code could not be created for the following person: {{ userDisplayName }} {{ userPrincipalName }} The creation was aborted because {{ userGivenName }} has already completed the onboarding process and has other authentication methods configured. {{ userGivenName }} must use the existing Temporary Access Pass to sign in before being able to use the other authentication methods. If {{ userGivenName }} is able to sign in using the active Temporary Access Pass, they can remove it themselves at https://aka.ms/MySecurityInfo to start using their other authentication methods. @@ -3099,7 +3099,7 @@ A Temporary Access Pass code could not be created for the following person: {{ userDisplayName }} {{ userPrincipalName }} The creation was aborted because {{ userGivenName }} has already completed the onboarding process and already has authentication methods configured. Temporary Access Pass codes must no longer be issued via the self-service process and require handling by IT support. Please ask {{ userGivenName }} to sign in using their existing authentication methods. -
jpawlowski revised this gist
Apr 26, 2025 . 1 changed file with 9 additions and 8 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -55,51 +55,52 @@ .PARAMETER EmailLanguage The language to use for the email notification. If not specified, the user's preferred language is used. The built-in template supports English (en), German (de), and French (fr). The parameter may also be set as an Automation Variable TAPConfig_EmailLanguage. .PARAMETER EmailSubject Custom email subject to use when sending notification to manager. If not specified, the default subject is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English subject", "de": "German subject", "fr": "French subject"} The parameter may also be set as an Automation Variable TAPConfig_EmailSubject. .PARAMETER EmailTitle Custom email title to use when sending notification to manager. If not specified, the default title is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English title", "de": "German title", "fr": "French title"} The parameter may also be set as an Automation Variable TAPConfig_EmailTitle. .PARAMETER EmailSalutation Custom email salutation to use when sending notification to manager. If not specified, the default salutation is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English salutation", "de": "German salutation", "fr": "French salutation"} The parameter may also be set as an Automation Variable TAPConfig_EmailSalutation. .PARAMETER EmailClosing Custom email closing phrase to use when sending notification to manager. If not specified, the default closing phrase is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English closing", "de": "German closing", "fr": "French closing"} The parameter may also be set as an Automation Variable TAPConfig_EmailClosing. .PARAMETER EmailBodyText Custom email body text to use when sending notification to manager. If not specified, the default body text is used. Variables may be used, see EmailTemplate parameter for available variables. Possible text variants are: "TAPCreated", "UserBlocked", "TAPNotEnabled", "TAPExpiredWithOtherMethodsConfigured", "TAPActiveWithOtherMethodsConfigured", "OtherMethodsConfigured". The value must be a JSON string with the format {"en": {"TAPCreated": "English text", "UserBlocked": "English text", ...}, "de": {"TAPCreated": "German text", "UserBlocked": "German text", ...}, ...} The parameter may also be set as an Automation Variable TAPConfig_EmailBodyText. .PARAMETER EmailTemplate Custom email template to use when sending notification to manager. Uses {{ variable }} syntax for variable substitution. Available variables: userGivenName, userName, userPrincipalName, managerName, temporaryAccessPass, lifetimeInMinutes, expirationTime, startDateTime, emailTitle, emailSalutation, emailClosing, emailBodyText, emailBodyAltertBanner, emailBodyFooterHint. Images can be embedded using {{ image:imageName }} placeholders. Multiple languages can be defined using the JSON format: {"en": "English template", "de": "German template", "fr": "French template"} The parameter may also be set as an Automation Variable TAPConfig_EmailTemplate. .PARAMETER EmailImagesJson -
jpawlowski revised this gist
Apr 26, 2025 . 1 changed file with 3328 additions and 1612 deletions.There are no files selected for viewing
-
jpawlowski revised this gist
Mar 19, 2025 . 1 changed file with 104 additions and 6 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -30,6 +30,7 @@ • Mail.Send - Ensure admin consent is granted for all required permissions. - Configure the 'AuthConfig_WebhookSignatureKey' Automation Variable with the shared secret used for HMAC validation. - Configure the 'TAPConfig_SenderEmailAddress' Automation Variable with the sender email address. .PARAMETER UserId User account identifier. May be an Entra Identity Object ID or User Principal Name (UPN). @@ -48,35 +49,42 @@ .PARAMETER SenderEmailAddress The email address of the sender. Can be a user or shared mailbox address. When run interactively, the sender address is automatically set to the signed-in user's email address. When running in Azure Automation, the sender address must either be provided directly, in the webhook configuration, or as Automation Variable TAPConfig_SenderEmailAddress. .PARAMETER EmailLanguage The language to use for the email notification. If not specified, the user's preferred language is used. The built-in template supports English (en) and German (de). The parameter may also be set as an Automation Variable TAPConfig_EmailLanguage. .PARAMETER EmailSubject Custom email subject to use when sending notification to manager. If not specified, the default subject is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English subject", "de": "German subject"} The parameter may also be set as an Automation Variable TAPConfig_EmailSubject. .PARAMETER EmailTitle Custom email title to use when sending notification to manager. If not specified, the default title is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English title", "de": "German title"} The parameter may also be set as an Automation Variable TAPConfig_EmailTitle. .PARAMETER EmailSalutation Custom email salutation to use when sending notification to manager. If not specified, the default salutation is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English salutation", "de": "German salutation"} The parameter may also be set as an Automation Variable TAPConfig_EmailSalutation. .PARAMETER EmailClosing Custom email closing phrase to use when sending notification to manager. If not specified, the default closing phrase is used. Variables may be used, see EmailTemplate parameter for available variables. Multiple languages can be defined using the JSON format: {"en": "English closing", "de": "German closing"} The parameter may also be set as an Automation Variable TAPConfig_EmailClosing. .PARAMETER EmailTemplate Custom email template to use when sending notification to manager. @@ -85,16 +93,19 @@ temporaryAccessPass, lifetimeInMinutes, expirationTime, startDateTime, EmailTitle, EmailClosing. Images can be embedded using {{ image:imageName }} placeholders. Multiple languages can be defined using the JSON format: {"en": "English template", "de": "German template"} The parameter may also be set as an Automation Variable TAPConfig_EmailTemplate. .PARAMETER EmailImagesJson A JSON string representing image references to embed in the email template. Format: {"imageName1": "base64string1", "imageName2": "base64string2"} Each image can be referenced in the template as {{ image:imageName1 }} Example: '{"logo": "data:image/png;base64,iVBORw0KG..."}' The parameter may also be set as an Automation Variable TAPConfig_EmailImagesJson. .PARAMETER UseHtmlEmail When specified, sends the email as HTML format. Default is true. Set to false to send plain text email. The parameter may also be set as an Automation Variable TAPConfig_UseHtmlEmail. .PARAMETER OutputJson Output the result in JSON format @@ -1019,12 +1030,99 @@ if ($LifetimeInMinutes) { } } if ($SendEmailToManager) { if ([string]::IsNullOrEmpty($SenderEmailAddress)) { if ('AzureAutomation/' -eq $env:AZUREPS_HOST_ENVIRONMENT -or $PSPrivateMetadata.JobId) { try { $SenderEmailAddress = Get-AutomationVariable -Name 'TAPConfig_SenderEmailAddress' -ErrorAction Stop } catch { throw 'SenderEmailAddress is required when sending email to manager.' } } else { $SenderEmailAddress = (Get-MgContext).Account Write-Verbose "Using authenticated user $SenderEmailAddress as sender email address" } } if ('AzureAutomation/' -eq $env:AZUREPS_HOST_ENVIRONMENT -or $PSPrivateMetadata.JobId) { if ([string]::IsNullOrEmpty($EmailLanguage)) { try { $EmailLanguage = Get-AutomationVariable -Name 'TAPConfig_EmailLanguage' -ErrorAction Stop } catch { # Use default language } } if ([string]::IsNullOrEmpty($EmailSubject)) { try { $EmailSubject = Get-AutomationVariable -Name 'TAPConfig_EmailSubject' -ErrorAction Stop } catch { # Use default subject } } if ([string]::IsNullOrEmpty($EmailTitle)) { try { $EmailTitle = Get-AutomationVariable -Name 'TAPConfig_EmailTitle' -ErrorAction Stop } catch { # Use default title } } if ([string]::IsNullOrEmpty($EmailSalutation)) { try { $EmailSalutation = Get-AutomationVariable -Name 'TAPConfig_EmailSalutation' -ErrorAction Stop } catch { # Use default salutation } } if ([string]::IsNullOrEmpty($EmailClosing)) { try { $EmailClosing = Get-AutomationVariable -Name 'TAPConfig_EmailClosing' -ErrorAction Stop } catch { # Use default closing } } if ([string]::IsNullOrEmpty($EmailTemplate)) { try { $EmailTemplate = Get-AutomationVariable -Name 'TAPConfig_EmailTemplate' -ErrorAction Stop } catch { # Use default template } } if ([string]::IsNullOrEmpty($EmailImagesJson)) { try { $EmailImagesJson = Get-AutomationVariable -Name 'TAPConfig_EmailImagesJson' -ErrorAction Stop } catch { # Use default images } } if ([string]::IsNullOrEmpty($UseHtmlEmail)) { try { $rawValue = Get-AutomationVariable -Name 'TAPConfig_UseHtmlEmail' -ErrorAction Stop if ($rawValue -is [bool]) { $UseHtmlEmail = $rawValue } else { try { $UseHtmlEmail = [System.Convert]::ToBoolean($rawValue) } catch { throw "Failed to convert TAPConfig_UseHtmlEmail value '$rawValue' to a boolean." } } } catch { # Use default HTML email setting } } } } # Get user information -
jpawlowski revised this gist
Mar 19, 2025 . No changes.There are no files selected for viewing
-
jpawlowski revised this gist
Mar 19, 2025 . 1 changed file with 224 additions and 123 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,9 +1,35 @@ <# .SYNOPSIS Create a Temporary Access Pass code for new hires that have not set up any Authentication Methods so far .DESCRIPTION This script is intended to be run as an Azure Automation Runbook or as a standalone script. Before generating a Temporary Access Pass, the script checks if the user has set up any Authentication Methods and has reached its hire date. If the user has not set up any Authentication Methods, a Temporary Access Pass is created. Depending on the configuration, the pass is output or sent to the user's manager via email. The email notification can be customized using a template with variables. Automatic email branding is supported by retrieving tenant branding images and information. Based on the manager's mailbox language, the email notification is sent in the appropriate language. As a fallback, the manager's preferredLanguage attribute is used. The script can be triggered by a webhook request, which is validated using an HMAC signature. The webhook request must include a JSON object with the UserId and may optionally include StartDateTime, LifetimeInMinutes, IsUsableOnce, and EmailLanguage. Other parameters are intentionally ignored to prevent sending unexpected emails. The webhook request must be signed using the shared secret key set as Automation Variable 'AuthConfig_WebhookSignatureKey'. An example function for the client request called 'Get-HmacSignedHeaders' can be found in the FUNCTIONS region down below. **Azure Automation & Managed Identity Setup Requirements:** - Run this script in an Azure Automation account using a system-assigned managed identity. - The managed identity must have the following Microsoft Graph API permissions: • User.Read.All • UserAuthenticationMethod.ReadWrite.All • Policy.Read.All • Directory.Read.All • MailboxSettings.Read • Mail.Send - Ensure admin consent is granted for all required permissions. - Configure the 'AuthConfig_WebhookSignatureKey' Automation Variable with the shared secret used for HMAC validation. .PARAMETER UserId User account identifier. May be an Entra Identity Object ID or User Principal Name (UPN). @@ -126,21 +152,197 @@ param ( ) #region FUNCTIONS function Get-HmacSignedHeaders { <# .SYNOPSIS Generates signed headers for HMAC authentication for Azure Automation webhook requests. .DESCRIPTION Generates signed headers for HMAC authentication based on a shared secret (provided as SecureString), webhook URL, and request body content. Includes timestamp, nonce, and content hash, all covered in the HMAC signature. SecureString is converted as late as possible and securely cleared after use. This function is intended to be used when calling Azure Automation webhooks or other APIs requiring signed requests for enhanced security. ------------------------------------------ 🔐 Secure Shared Secret Retrieval Options: ------------------------------------------ Always retrieve secrets securely from encrypted sources, ensuring they are stored and used as SecureStrings: 1️⃣ Azure Key Vault (Recommended for cloud/hybrid environments) Connect-AzAccount -Identity $sharedSecret = (Get-AzKeyVaultSecret -VaultName "<YourVaultName>" -Name "HmacSharedSecret").SecretValue 2️⃣ Azure Automation Encrypted Variable (inside Automation Account only) $sharedSecret = (Get-AutomationVariable -Name "SharedSecret") | ConvertTo-SecureString -AsPlainText -Force 3️⃣ Windows Credential Manager (if running on Windows, via SecretManagement module) Import-Module Microsoft.PowerShell.SecretManagement $sharedSecret = (Get-StoredCredential -Target "HmacSharedSecret").Password 4️⃣ Encrypted local file (protected by ACLs; not recommended for cloud, acceptable in controlled environments) $sharedSecret = (Get-Content -Path "C:\Secrets\HmacSecret.txt" -Raw) | ConvertTo-SecureString -AsPlainText -Force ❌ For demonstration purposes only (NEVER hardcode secrets in production): $sharedSecret = ConvertTo-SecureString -String "MySuperSecretKey" -AsPlainText -Force .EXAMPLE # Example usage: $sharedSecret = (Get-AutomationVariable -Name "SharedSecret") | ConvertTo-SecureString -AsPlainText -Force $webhookUrl = "https://<your-webhook-url>/webhooks" $body = '{"param1":"value1"}' $headers = Get-HmacSignedHeaders -SharedSecret $sharedSecret ` -WebhookUrl $webhookUrl ` -Body $body [void]$sharedSecret.Dispose() Invoke-RestMethod -Method POST ` -Uri $webhookUrl ` -Headers $headers ` -Body $body ` -ContentType 'application/json; charset=utf-8' .FUNCTIONALITY Security, HMAC Authentication .NOTES Author: Julian Pawlowski Company Name: Workoho GmbH Created: 2025-03-17 Updated: 2025-03-18 #> param( # Shared secret used for HMAC signature (SecureString) [Parameter(Mandatory)][securestring]$SharedSecret, # Full webhook URL to which the request will be sent [Parameter(Mandatory)][string]$WebhookUrl, # Request body content as a string (usually JSON or similar) [Parameter(Mandatory)][string]$Body, # Algorithm to use for HMAC signature (default: HMACSHA256) [string]$Algorithm = "HMACSHA256", # Optional: Provide custom nonce (for testing/debugging); defaults to a new random GUID if omitted [string]$Nonce ) if ($Algorithm -notin @('HMACSHA256', 'HMACSHA512')) { throw "Unsupported algorithm: $Algorithm" } # Parse URL components $uri = [System.Uri]$WebhookUrl $method = "POST" $path = $uri.AbsolutePath $webHost = $uri.Host # Timestamp header (RFC1123 format) $date = (Get-Date).ToUniversalTime().ToString("R") # Compute body hash (SHA256) $bodyBytes = [Text.Encoding]::UTF8.GetBytes($Body) $contentHash = [Convert]::ToBase64String(([System.Security.Cryptography.SHA256]::Create()).ComputeHash($bodyBytes)) # Generate nonce if not provided if (-not $Nonce) { $Nonce = [Guid]::NewGuid().ToString() } # Define signed headers and order $signedHeadersList = @('x-ms-date', 'Host', 'x-ms-content-sha256', 'x-ms-nonce') $signedHeaders = ($signedHeadersList -join ';') # Build canonical message $canonicalMessage = "$method`n$path`n$date;$webHost;$contentHash;$Nonce" $unsecureSecret = $null try { $hmac = New-Object ("System.Security.Cryptography.$Algorithm") # SecureString → plaintext $unsecureSecret = [System.Net.NetworkCredential]::New("", $SharedSecret).Password # Set key and compute signature $hmac.Key = [Text.Encoding]::UTF8.GetBytes($unsecureSecret) $signature = [Convert]::ToBase64String($hmac.ComputeHash([Text.Encoding]::UTF8.GetBytes($canonicalMessage))) # Cleanup HMAC key [Array]::Clear($hmac.Key, 0, $hmac.Key.Length) $hmac = $null } finally { if ($unsecureSecret) { [Array]::Clear($unsecureSecret.ToCharArray(), 0, $unsecureSecret.Length) $unsecureSecret = $null } } # Prepare headers (Host excluded intentionally) $headers = @{ 'x-ms-date' = $date 'x-ms-content-sha256' = $contentHash 'x-ms-nonce' = $Nonce 'x-authorization' = "HMAC-$($Algorithm.Replace('HMAC','')) SignedHeaders=$signedHeaders&Signature=$signature" } return $headers } function Test-HmacAuthorization { <# .SYNOPSIS Verifies HMAC signature of incoming Azure Automation webhook requests. .DESCRIPTION Validates HMAC signature based on signed request headers (timestamp, nonce, content hash) and a shared secret. Supports both HMACSHA256 and HMACSHA512 algorithms. The shared secret is passed securely as a SecureString, converted as late as possible, and cleared from memory immediately after use. Signature verification includes: - Timestamp freshness validation (anti-replay window configurable via AllowedTimeDriftMinutes) - Host - Body content hash integrity check - Nonce inclusion for replay protection (future extension to store/check nonce is left open) .EXAMPLE # ✅ Example 1: Production (Azure Automation) # Retrieve encrypted variable securely $sharedSecret = (Get-AutomationVariable -Name "SharedSecret") | ConvertTo-SecureString -AsPlainText -Force # Verify signature if (-not (Test-HmacAuthorization -SharedSecret $sharedSecret -WebhookData $WebhookData)) { [void]$sharedSecret.Dispose() throw "Unauthorized: Signature verification failed." } # Dispose secret after use [void]$sharedSecret.Dispose() .EXAMPLE # ✅ Example 2: Local Development / Testing # Use test shared secret (DO NOT use hardcoded secrets in production) $sharedSecret = ConvertTo-SecureString -String "MyTestSecretKey" -AsPlainText -Force # Verify signature if (-not (Test-HmacAuthorization -SharedSecret $sharedSecret -WebhookData $WebhookData)) { Write-Output "❌ Signature invalid." } else { Write-Output "✅ Signature valid." } # Dispose secret [void]$sharedSecret.Dispose() .FUNCTIONALITY Security, HMAC Authentication .NOTES Author: Julian Pawlowski @@ -193,6 +395,12 @@ function Test-HmacAuthorization { } } # Host header check if ([string]::IsNullOrEmpty($headers.'Host')) { Write-Error 'Host header required' return $false } # Timestamp freshness check if ([string]::IsNullOrEmpty($headers.'x-ms-date')) { Write-Error 'x-ms-date header required' @@ -268,113 +476,6 @@ function Test-HmacAuthorization { ) } function Assert-ParameterType { param ( [Parameter(Mandatory = $true)] @@ -823,8 +924,8 @@ $mgScopes = [string[]]@( # Make sure Mail.Send permission is included if SendEmailToManager is requested if ($SendEmailToManager) { $mgScopes += 'MailboxSettings.Read' # To read mailbox language $mgScopes += 'Mail.Send' # To send email on behalf of a shared mailbox } # User validation - reject specific account types @@ -1294,7 +1395,7 @@ if ($SendEmailToManager) { } else { try { $EmailSubjects = ConvertFrom-Json -InputObject $EmailSubject -AsHashtable -ErrorAction Stop if ($EmailSubjects -is [hashtable]) { if ($EmailSubjects.ContainsKey($languageCode)) { $EmailSubject = $EmailSubjects[$languageCode] @@ -1320,7 +1421,7 @@ if ($SendEmailToManager) { } else { try { $EmailTitles = ConvertFrom-Json -InputObject $EmailTitle -AsHashtable -ErrorAction Stop if ($EmailTitles -is [hashtable]) { if ($EmailTitles.ContainsKey($languageCode)) { $EmailTitle = $EmailTitles[$languageCode] @@ -1346,7 +1447,7 @@ if ($SendEmailToManager) { } else { try { $EmailSalutations = ConvertFrom-Json -InputObject $EmailSalutation -AsHashtable -ErrorAction Stop if ($EmailSalutations -is [hashtable]) { if ($EmailSalutations.ContainsKey($languageCode)) { $EmailSalutation = $EmailSalutations[$languageCode] @@ -1372,7 +1473,7 @@ if ($SendEmailToManager) { } else { try { $EmailClosings = ConvertFrom-Json -InputObject $EmailClosing -AsHashtable -ErrorAction Stop if ($EmailClosings -is [hashtable]) { if ($EmailClosings.ContainsKey($languageCode)) { $EmailClosing = $EmailClosings[$languageCode] @@ -2124,7 +2225,7 @@ if ($SendEmailToManager) { } else { try { $EmailTemplates = ConvertFrom-Json -InputObject $EmailTemplate -AsHashtable -ErrorAction Stop if ($EmailTemplates -is [hashtable]) { if ($EmailTemplates.ContainsKey($languageCode)) { Write-Verbose "Using custom language-specific HTML email template for $languageCode" @@ -2220,7 +2321,7 @@ Tenant ID: {{ orgTenantId }} } else { try { $EmailTemplates = ConvertFrom-Json -InputObject $EmailTemplate -AsHashtable -ErrorAction Stop if ($EmailTemplates -is [hashtable]) { if ($EmailTemplates.ContainsKey($languageCode)) { $bodyContentTemplate = $EmailTemplates[$languageCode] -
jpawlowski revised this gist
Mar 19, 2025 . No changes.There are no files selected for viewing
-
jpawlowski revised this gist
Mar 19, 2025 . No changes.There are no files selected for viewing
-
jpawlowski created this gist
Mar 19, 2025 .There are no files selected for viewing