Silently set “Outlook On The Web” / “Outlook New” signatures when the signature content changes or every 8 hours.
Scope of Support and Responsibilities
This tool integrates with Azure API Management (APIM) and Azure Functions. The provisioning, configuration, security, and ongoing management of Azure APIM and Azure Function Apps fall outside the scope of WPSecure package licensing and support.
The integration is provided to enable interoperability with external cloud infrastructure and to offer implementation flexibility. Any issues related to Azure APIM or Azure Functions—including availability, security configuration, scalability, or platform-specific behavior—should be directed to Microsoft Support.
Implementation Guidance
All components delivered as part of the WPSecure Personalization Packages are designed to be straightforward to deploy and manage. However, configuring Azure APIM and Azure Functions requires specialized Azure expertise.
This document should be treated as baseline reference guidance only. Customers are responsible for implementing their own security controls, governance policies, and organizational standards in accordance with their Azure architecture and compliance requirements.
This setup is not required if your organization primarily uses Outlook Classic as an email client; you will only need the “New message signature template” and the “Reply message signature template“. If cloud roaming is enabled (it is by default), Outlook Classic will automatically sync email signatures to the Cloud.
However, suppose your organization primarily uses Outlook New or Outlook Web as an email client, and Outlook Classic is not used. In that case, you will only need the “Web message signature template” and can proceed with the next steps.
This article will guide you through setting up an Azure Function fronted by an Azure APIM Service that will process and set the Exchange Online default signature based on the following files created by the WPSecure signature processing engine on the local computer.
wpsecure_web.htm | This file will be set as the default signature for messages in a HTM format for Outlook New and Outlook On The Web |
wpsecure_web.txt | (Optional) This file will be set as the default signature for messages in a TXT format for Outlook New and Outlook On The Web |
Prerequisites and Required Expertise
To successfully complete the procedures outlined in this document, the following permissions are required:
Exchange Online Administrator | Turn off signature roaming and to provide permissions to the Azure Functions Managed Identity. |
Azure Global Administrator | Provide correct Microsoft Graph permissions for the Azure Functions Managed Identity. |
Execution of the procedures described below requires the involvement of an experienced Azure administrator, particularly one with expertise in Azure API Management (APIM) gateways and Azure Functions. These procedures assume a strong working knowledge of Azure security concepts, identity and access management, and industry-standard cloud networking practices.
Technology touchpoints
The configuration of Outlook (New) and Outlook on the Web signatures through Exchange Online involves the following technology touchpoints.
WPSecure engine | Retrieves the user‑specific web signature and invokes the Azure APIM endpoint using a client authentication certificate attached to the request. |
Azure APIM API | The backend Azure Function is protected by Azure API Management (APIM). This architecture enables the application of layered security policies that block all traffic except for requests that are explicitly authorized and required. |
The Azure Function | The Azure Function acts as the core computational component of the solution architecture. It receives requests from the WPSecure engine via the Azure API Management (APIM) layer, processes and formats the required data, and applies both HTML and plain‑text email signatures to user mailboxes in Exchange Online. From a security standpoint, the Azure Function must not be directly accessible. It should be configured to accept inbound requests exclusively from the Azure APIM endpoint, ensuring that all traffic is subject to APIM‑enforced authentication, validation, and policy governance. |
Azure APIM Service
While Azure API Management (APIM) is not the only security control available for protecting Azure Functions, it commonly serves as the primary protective layer. APIM acts as a managed gateway, preventing direct access to backend functions and reducing the risk of unauthorized access or exploitation. In enterprise environments, it is a standard practice to further secure APIM by placing it behind Azure Front Door to provide an additional layer of global edge security, traffic filtering, and resiliency.
For the purposes of this documentation, the Azure API Management Consumption tier is used. However, for production workloads, it is recommended to use the Standard, or Premium tiers. These tiers offer enhanced scalability, reliability, networking options (vNet/express route), and advanced operational capabilities that are better suited to enterprise-grade and production environments.
| Security Type | Explanation |
|---|---|
| mTLS (Mutual TLS) | Ensures only clients with a valid client authentication certificate from a trusted CA can connect by validating the full certificate chain. |
| VNet / ExpressRoute | Keeps traffic between clients, the API, and the Azure Function on private network paths, preventing any exposure to the public internet. |
| UPN validation in inbound rules | Inbound policy extracts the UPN from the client certificate's SAN and compares it with the UPN in the request body to ensure identity integrity. |
| Rate limiting | Protects the API and Azure Function by controlling the number of requests a client can send within a defined timeframe, preventing abuse and ensuring fair usage. |
This GitHub file illustrates the structure of the Azure APIM inbound rule and is provided for illustration purposes only. Click here to open the GitHub repository.
We will not cover the detailed implementation or full configuration of an Azure API Management (APIM) service in this document. However, there are a small number of recommended default settings that we strongly suggest configuring out of the box to establish a secure and well‑behaved baseline even during the development and testing phase.
After creating an Azure API Management (APIM) service within your subscription, navigate to Deployment and infrastructure → Custom domains, and set Request client certificate to Yes.
Enabling this setting configures APIM to require the calling client to present a TLS client certificate during the connection handshake. While this configuration does not perform full certificate chain or issuer validation on its own, it ensures that a client certificate is provided before any request is accepted or processed by APIM.
As noted earlier, the APIM Consumption tier—used for the purposes of this demonstration—does not support advanced mutual TLS (mTLS) validation capabilities, such as trusted certificate authority enforcement or full certificate chain validation. For production and enterprise workloads, it is strongly recommended to use a higher‑tier APIM plan (Standard, or Premium), which provides comprehensive mTLS validation, enhanced security controls, and advanced networking features suitable for secure, large‑scale deployments.
During development and testing phases, it is recommended to explicitly allow your current public IP address by adding it to an IP filtering [Allow] rule. This ensures controlled access to the API while avoiding unnecessary exposure during early implementation stages.
The WPSecure engine will consistently present the appropriate client authentication certificate with each request. This enables APIM to enforce the presence of a client certificate and, when higher-tier APIM plans are used, perform full certificate chain validation and trusted issuer verification as part of the request processing pipeline.
Client Authentication Certificate
A Client Authentication SCEP certificate can be deployed either by using an on‑premises or internal Certificate Authority (CA), or by leveraging Microsoft Intune Cloud PKI to issue and deploy the certificate through an Intune SCEP certificate profile.
A client authentication certificate containing the Extended Key Usage (EKU) value Client Authentication (OID 1.3.6.1.5.5.7.3.2) can be used to securely invoke an Azure Function exposed through Azure API Management (APIM) by implementing mutual TLS (mTLS).
In this architecture, the client presents an X.509 certificate that includes the Client Authentication EKU when initiating a TLS connection to the APIM endpoint. APIM enforces the requirement for a client certificate during the TLS handshake and, in supported tiers, validates the certificate against an approved Certificate Authority (CA). Only requests authenticated with a valid client certificate containing the appropriate EKU are allowed to proceed.
Once successfully authenticated, APIM forwards the request to the backend Azure Function. The function itself is not directly exposed to the public internet and relies on APIM as its primary security boundary, thereby reducing the attack surface and centralizing access control and policy enforcement.
It is important to emphasize that while the APIM Consumption tier—used in this article for demonstration purposes—can enforce the presence of a client certificate, it does not support full certificate chain validation or trusted issuer enforcement. For production and enterprise workloads, it is strongly recommended to use a Basic, Standard, or Premium APIM tier, which provides comprehensive mTLS validation, enhanced security capabilities, and greater control over certificate trust and validation policies.
In addition to the required Client Authentication Extended Key Usage (EKU)
(OID 1.3.6.1.5.5.7.3.2), the user’s User Principal Name (UPN) must be included in the certificate’s Subject Alternative Name (SAN) extension.
The UPN can be expressed in the SAN using either of the following supported formats:
- Other Name → Principal Name
- RFC 822 Name
As illustrated in the image below, either method enables explicit binding of the certificate to a specific user or identity.
Before any request is sent to the Azure API Management (APIM) gateway, the WPSecure engine performs a strict identity validation check. Specifically, it verifies that the configured user identifier exactly matches the Principal Name value contained within the certificate’s SAN extension.
If the configured identifier and the SAN Principal Name do not match precisely, the request is immediately blocked and not forwarded to the Azure APIM service. This enforcement ensures that only certificates explicitly issued for the intended user or identity are permitted to initiate API calls, preventing certificate misuse or impersonation.
The Principal Name in the SAN extension should match the UPN output of the following command.
whoami /upn
For ease of administration and reliable certificate selection, it is recommended that the certificate Subject Name (Common Name — CN) clearly reflects its intended purpose and follows a consistent naming convention across all environments and devices.
A well‑defined and standardized CN simplifies operational management, reduces the risk of incorrect certificate selection, and improves traceability during troubleshooting and security audits. In this implementation, the Subject Name (CN) is referenced by command‑line tools to select and present the appropriate client certificate when establishing a connection to the Azure API Management–protected Azure Function, as discussed later in this document.
As a final requirement, the client authentication certificate must be installed in the user’s Personal certificate store. This ensures that the certificate is accessible to the WPSecure engine for initiating authenticated connections to the Azure APIM service.
Create a Function App in Azure Portal
When deploying Azure Functions, it is recommended to use the Premium Plan for production workloads. The Premium Plan provides consistent performance and eliminates cold start behavior, making it well suited for latency‑sensitive and continuously used applications.
The Flex Consumption plan may experience cold starts, which can introduce latency during periods of inactivity. However, if cold start delays are acceptable for your use case, the Flex Consumption plan can also be used as a cost‑effective alternative.
Function Key (Only discussed) | Function keys provide a basic access control mechanism that helps ensure only authorized users or systems can invoke an Azure Function, adding an additional layer of protection. However, function keys alone do not secure a Function App and should not be relied upon as the primary security control. They are most effective when used in combination with other security measures such as network isolation, and API gateway protections (for example, Azure APIM). For the purposes of this demonstration, the Azure Function is secured using a Function Key only. |
Private Network Access Model | By integrating both Azure API Management and the Azure Function App with the same Virtual Network—or peered VNets—traffic between APIM and the Function can be routed entirely through private network paths. This allows the Azure Function to be exposed only via private endpoints or internal addresses, eliminating public endpoint access. |
Having discussed the fundamentals, it is now time to create the Azure Function and make the necessary configuration changes.
Create a resource in Azure and select ‘Function App‘ as the service type.
Select either Flex Consumption or Function Premium with the Windows OS option. However, select Function Premium to prevent cold starts.
In the next step, please select a name for the function that will enable Outlook web/new signature updates.
We recommend using the format wpsecure-awsu-****, where **** is a unique random number specific to your organization (between 4 and 8 digits).
For consistency, use the same number across all other WPSecure-related Azure Functions that you configure.
In the next step, select a storage account.
If you already have an existing storage account that meets the requirements, you may choose to reuse it.
To simplify service association and maintain consistency, we recommend incorporating the same unique number from the previous step (e.g., naming the storage account wpsecurefunctions**** or similar, using your chosen 4–8 digit identifier). This helps keep all related WPSecure components easily identifiable and linked.
For production systems, do not enable public access to the Azure Function.
If public access is unavoidable during the initial configuration (e.g., due to temporary constraints), you may select Public only for the setup and testing phase.
Once the setup is complete and you have successfully verified that the connections are working correctly:
- Navigate to the Function App settings.
- Enable Virtual Network Integration.
- Select the same Virtual Network (VNet) and subnet used by your Azure API Management (APIM) service.
This configuration ensures that the only inbound connections allowed to the Azure Function are those originating from your Azure APIM service, effectively restricting public exposure in production.
To effectively troubleshoot issues with your Azure Function (e.g., verifying whether requests are reaching it from Azure API Management, identifying processing failures, and taking corrective actions), enable Azure Monitor Application Insights during or after deployment.
Application Insights provides deep visibility into:
- Incoming requests: Track request volume, sources (including APIM), response times, and success/failure rates.
- Request processing details: View end-to-end traces, dependencies, and custom events/logs to understand how the function handles each request.
- Failures and exceptions: Automatically capture failed requests, exceptions, stack traces, and error details for rapid diagnosis.
- Performance anomalies: Detect slow responses, high failure rates, or unusual patterns in real time
Continuous deployment settings and the tags should be based on your organization’s needs. Finally, review and Create.
When the deployment is complete, go to the resource.
Copy Function App Managed Identity ID
Go to Settings > Identity in the left-hand menu. Under the System assigned tab, set the Status to On. Click Save. After saving, the Object ID (also referred to as the Principal ID) will appear on the same page. Copy this Object (Principal) ID and paste it into Notepad (or your preferred text editor) for use in subsequent steps.
Exchange Online related tasks
Open Powershell on your computer and run the following commands. You will need the ExchangeOneline PowerShell module installed to run the below commands. You will have to run the below commands as an Exchange Administrator. Copy and paste the value corresponding to the ‘OrganizationalUnitRoot’ attribute to Notepad for future reference.
Install-Module -Name ExchangeOnlineManagement
Connect-ExchangeOnline
Get-OrganizationalUnit
Grant the required permissions to the Managed Identity.
We need to grant the Function App’s managed identity ‘Exchange Administrator’ permissions. The permissions can be granted using the script below. Before executing the script, you need to do the following:
- Install the Microsoft Graph SDK if you haven’t already done so. That happens by executing the following PowerShell command :
Install-Module Microsoft.Graph -Scope CurrentUser - Set your Azure environments tenant ID and the managed identity (Principal) object ID you copied earlier as the script’s input variables.
Run the script and ensure there are no errors.
$tenantId = ""
$managedIdentityId = ""
Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All, Application.Read.All, RoleManagement.ReadWrite.Directory -TenantId $tenantId -NoWelcome
$resourceId = (Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'").Id
$appRoleId = "dc50a0fb-09a3-484d-be87-e023b12c6440"
$roleDefinitionId = (Get-MgRoleManagementDirectoryRoleDefinition -Filter "DisplayName eq 'Exchange Administrator'").Id
# Check if the app role is already assigned
$existingAppRoles = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityId
if ($existingAppRoles) {
Write-Host "The Managed identity has existing roles. Adding missing roles (If any). (MgServicePrincipalAppRoleAssignment)."
foreach ($existingAppRole in $existingAppRoles) {
if ($existingAppRole.AppRoleId -notcontains $appRoleId) {
Write-Host "New role $appRoleId will be added (MgServicePrincipalAppRoleAssignment)."
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityId -PrincipalId $managedIdentityId -AppRoleId $appRoleId -ResourceId $resourceId
}
}
} else {
Write-Host "The Managed identity does not have existing roles (MgServicePrincipalAppRoleAssignment). New Item will be created."
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $managedIdentityId -PrincipalId $managedIdentityId -AppRoleId $appRoleId -ResourceId $resourceId
}
# Check if the directory role is already assigned
$existingRoleAssignments = Get-MgRoleManagementDirectoryRoleAssignment -Filter "PrincipalId eq '$managedIdentityId'"
if ($existingRoleAssignments) {
Write-Host "The Managed identity has existing role assignments. Adding missing role assignments (If any). (MgRoleManagementDirectoryRoleAssignment)."
foreach ($existingRoleAssignment in $existingRoleAssignments) {
if ($existingRoleAssignment.RoleDefinitionId -notcontains $roleDefinitionId) {
Write-Host "New role assignment $existingRoleAssignment.RoleDefinitionId will be added (MgServicePrincipalAppRoleAssignment)."
New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $managedIdentityId -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/"
}
}
} else {
Write-Host "The Managed identity does not have existing role assignments (MgRoleManagementDirectoryRoleAssignment). New Item will be created."
New-MgRoleManagementDirectoryRoleAssignment -PrincipalId $managedIdentityId -RoleDefinitionId $roleDefinitionId -DirectoryScopeId "/"
}
Create a function within the Azure Function App
The deployment method for Azure Functions varies depending on the pricing tier (Consumption, Premium, or Dedicated/App Service Plan). To simplify the process and ensure consistency across all tiers, we have provided a pre-written Function in our GitHub repository.
- Navigate to our GitHub repository – https://github.com/osd365/wpsecure-azure-functions
- Download or clone the repository to your local machine.
After cloning or extracting the files, the folder structure will look similar to the image below. You will see three functions, each represented by its own folder. Select the folder that is highlighted in the image below.
Change directory to the above displayed directory.
Compress the required contents into a single ZIP archive named autoSetWebSignature.zip.
The autoWebSignature.zip file must contain the following items at the root level of the archive:
- The
autoSetWebSignature/directory - The
modules/directory - The
host.jsonfile - The
profile.ps1file
Ensure that these files and folders are included directly in the ZIP package and not nested within an additional parent directory.
The ZIP file can now be uploaded to your Azure Function. In the Azure portal, navigate to your Function App, then go to Deployment Center. Select Publish Files (New), click Browse, and choose the ZIP file you created in the previous step.
Make sure you click ‘Save‘ to begin the upload and import process.
In the Overview tab, you should now see your deployed function(s) listed under the Functions section (it may take a few moments to appear).
Click the function name to open its details, navigate to the Code + Test tab to review the PowerShell code in run.ps1, then switch to the Function Keys tab, copy the default function key, and paste it into Notepad (or a secure location) for future use.
using namespace System.Net
using namespace System.Web
# Input bindings are passed in via param block.
param($Request, $TriggerMetadata)
# Write to the Azure Functions log stream.
Write-Host "A new process has started to set Exchange email signature for a User."
# Implement additional validation logic for Exchange Online signature update requests here.
# Verify that the UPN extracted from the client certificate matches the UPN supplied by the calling device.
# Inspect and validate certificate attributes to ensure the request originates from a trusted and compliant source.
# The code below provides a functional baseline; consider extending it with further security controls to harden this Azure Function.
try {
$EXCHANGE_ORGANIZATIONAL_UNIT_ROOT = $env:EXCHANGE_ORGANIZATIONAL_UNIT_ROOT
if (-not [string]::IsNullOrEmpty($EXCHANGE_ORGANIZATIONAL_UNIT_ROOT) -and $EXCHANGE_ORGANIZATIONAL_UNIT_ROOT -ne "") {
# Get signature related environment variables
$AUTO_ADD_SIGNATURE_ON_NEW_MESSAGE = $true
$AUTO_ADD_SIGNATURE_ON_REPLY_MESSAGE = $true
try {$AUTO_ADD_SIGNATURE_ON_NEW_MESSAGE = [bool]::Parse($env:AUTO_ADD_SIGNATURE_ON_NEW_MESSAGE) } catch {$AUTO_ADD_SIGNATURE_ON_NEW_MESSAGE = $true} # Set as detault signature for New messages?
try {$AUTO_ADD_SIGNATURE_ON_REPLY_MESSAGE = [bool]::Parse($env:AUTO_ADD_SIGNATURE_ON_REPLY_MESSAGE) } catch {$AUTO_ADD_SIGNATURE_ON_REPLY_MESSAGE = $true} # Set as detault signature for Reply messages?
Import-Module ExchangeOnlineManagement -Force
Connect-ExchangeOnline -ManagedIdentity -Organization $EXCHANGE_ORGANIZATIONAL_UNIT_ROOT
$upn = $Request.Body.upn;
if (-not [string]::IsNullOrEmpty($upn) -and $upn -ne "") {
$signatureHtml = [System.Web.HttpUtility]::HtmlDecode($Request.Body.signatureHtml)
$signatureText = [System.Web.HttpUtility]::HtmlDecode($Request.Body.signatureText);
Write-Host "Starting the process to set Exchange email signature for mailbox with UPN => $upn."
if ((-not [string]::IsNullOrEmpty($signatureHtml)) -and ($signatureHtml -ne "") -and (-not [string]::IsNullOrEmpty($signatureText)) -and ($signatureText -ne "")) {
Write-Host "Setting HTML and TXT signatures for UPN => $upn."
Set-MailboxMessageConfiguration -Identity $upn -SignatureHTML $signatureHtml -SignatureText $signatureText
Write-Host "Finished setting HTML and TXT signature for UPN => $upn."
} elseif ((-not [string]::IsNullOrEmpty($signatureHtml)) -and ($signatureHtml -ne "")) {
Write-Host "Setting HTML signature for UPN => $upn."
Set-MailboxMessageConfiguration -Identity $upn -SignatureHTML $signatureHtml
Write-Host "Finished setting HTML signature for UPN => $upn."
} elseif ((-not [string]::IsNullOrEmpty($signatureText)) -and ($signatureText -ne "")) {
Write-Host "Setting TXT signature for UPN => $upn."
Set-MailboxMessageConfiguration -Identity $upn -SignatureText $signatureText
Write-Host "Finished setting TXT signature for UPN => $upn."
} else {
Write-Host "Signature files empty or NULL. Did not set HTML and TXT signatures for UPN => $upn."
}
Write-Host "Setting AUTO signature application settings for UPN => $upn."
Set-MailboxMessageConfiguration -Identity $upn -AutoAddSignature $AUTO_ADD_SIGNATURE_ON_NEW_MESSAGE -AutoAddSignatureOnReply $AUTO_ADD_SIGNATURE_ON_REPLY_MESSAGE
Write-Host "Finished setting AUTO signature application settings for UPN => $upn."
}
else {
Write-Host "Error: UPN not identified."
# NEW: Return a 400 Bad Request when required configuration is missing
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = @{ error = "Error: UPN not identified." }
Headers = @{ "Content-Type" = "application/json" }
})
return
}
} else {
Write-Host "Error: No EXCHANGE_ORGANIZATIONAL_UNIT_ROOT value supplied for this transaction."
# NEW: Return a 400 Bad Request when required configuration is missing
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::BadRequest
Body = @{ error = "Error: No EXCHANGE_ORGANIZATIONAL_UNIT_ROOT value supplied for this transaction." }
Headers = @{ "Content-Type" = "application/json" }
})
return
}
}
catch {
$ex = $_.Exception
Write-Host "Exception: The process failed. $($ex.Message)"
Write-Error "Stack Trace: $($ex.StackTrace)" # Uncomment for detailed troubleshooting.
Write-Error "Inner Exception: $($ex.InnerException)" # Uncomment for detailed troubleshooting.
# Return a 500 Internal Server Error to the caller
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
StatusCode = [HttpStatusCode]::InternalServerError
Headers = @{ "Content-Type" = "application/json" }
Body = @{
error = "An error occurred while processing the Outlook web signature update request."
message = $ex.Message
# Optional: include diagnostics when you need them
# stackTrace = $ex.StackTrace
# innerMessage = $ex.InnerException?.Message
# requestId = $TriggerMetadata?.InvocationId
}
})
}
finally {
Disconnect-ExchangeOnline -Confirm:$false
Get-PSSession | Remove-PSSession
}
Navigate to the API / API Management section, select the API Management service you created earlier (or an existing one), leave API set to Create new, and on the next screen, accept all the default settings for linking the API. Click Link API.
Set the header value for x-functions-key by using the function key you copied in the previous steps. Click Save.
Click on the Settings tab, copy the Base URL for future use, and then ensure that the Subscription Required option is UNCHECKED. Click Save.
Set Azure Function App Environment variables
Move to the main page of the Function App. Go to ‘Settings/Environmental variables‘
We will have to input 3 environmental variables into the Azure Function App.
AUTO_ADD_SIGNATURE_ON_NEW_MESSAGE | Set this value to TRUE. |
AUTO_ADD_SIGNATURE_ON_REPLY_MESSAGE | Set this value to TRUE. |
EXCHANGE_ORGANIZATIONAL_UNIT_ROOT | Copy and paste this value from Notepad. We got this value in one of the previous steps. yourorgvalue.onmicrosoft.com |
Enabling or Disabling Automatic Silent Outlook Web Signature Synchronization
By default, automatic web signature sync is disabled. To enable it, run the following command as an administrator. The executable is in the WPSecure application directory within the ProgramFiles (x86) directory.
"C:\Program Files (x86)\wpsecure\wpsecure-set.exe" -awsuuri -subjectname WPSecureAzureFunctionAuthenticationCertificate24 -version 5.3.56
-awsuuri | From notepad copied previously: The base URL of the API |
-subjectname | The Subject name of the certificate (sometimes called the Subject Common Name) issued by Cloud PKI or your Internal CA. Note: The Subject name should remain the same across all devices, while the Subject Alternative Name (SAN) must contain the UPN of the user. |
-version | Registry detection after deployment (See below) |
To detect the setting’s presence, set your detection methods to look for the following registry value and use the operator “greater than or equal to” to compare the value as a TYPE: Version.
To disable, run the following command as an administrator.
"C:\Program Files (x86)\wpsecure\wpsecure-set.exe" -dawsu
Operational Behavior and Troubleshooting Guidance
Once the solution has been successfully configured, the process operates fully automatically. Signature synchronization runs whenever the local copy of the signature is updated, or on a scheduled interval of every eight hours, without requiring user interaction.
Reaching a fully autonomous state, however, can be challenging during initial setup. In these scenarios, the Azure Function Log stream becomes an essential tool for diagnosing and resolving issues.
If the solution does not behave as expected, review the following areas:
- Devices must be successfully authenticated and authorized by the Azure API Management (APIM) service before they can access the Azure Function.
- The APIM service must be configured with the correct Azure Function key (also referred to as the function code) when invoking the function.
- If no entries appear in the Function App Log stream, the device has not successfully authenticated or the request is being blocked before reaching the function.
- All required PowerShell modules must load successfully during function execution.
- The system‑assigned managed identity associated with the Azure Function must have sufficient permissions to access Exchange Online.
- The client authentication certificate must include the user’s UPN in the Subject Alternative Name (SAN) extension.
- The APIM service must be configured to always request a client certificate to ensure mutual TLS (mTLS) enforcement.
Client‑side diagnostic logs are written to the file wpsecure-set.log, located in the %TEMP% directory on the user’s device. These logs are useful for validating local execution, certificate selection, and request initiation prior to transmission.
In summary, the solution establishes a secure, end‑to‑end workflow that begins with a client‑side request and is routed through Azure API Management (APIM), where authentication, policy enforcement, and traffic management are applied. APIM then invokes the Azure Function, which executes the required custom logic (for example, PowerShell‑based automation). Finally, the process integrates with Exchange Online to apply and update web message signatures for users. This architecture centralizes control, enforces security boundaries, and enables reliable, automated signature synchronization across environments.
After you have successfully implemented everything covered in this document, ensure you start tightening the security—the as-is setup is not sufficient for a production implementation. Work with your Azure administrator to secure the connection from clients to the Azure APIM Service, as well as the connection between the Azure APIM Service and the Azure Function. After each security enhancement, verify that the solution remains fully functional.
