Skip to content

Preparing technical configuration for DPG Service

This document describes the required technical configuration for new DPG customers with Exchange Online.

The configuration must be performed by the customer, and it is the responsibility of the designated Technical Contact to act as a single point of contact and to coordinate all necessary customer resources during the DPG implementation project.

Mailboxes: Naming, users, and permissions

  • A DPG system mailbox must be created as a licensed mailbox. It is recommended to name it dpg@<CUSTOMER.NAME>.
  • One or more secure mailboxes (a.k.a. funktionspostkasser) must already exist or be created. They are typically named sikkerpost@<CUSTOMER.NAME>, hr@<CUSTOMER.NAME>, finance@<CUSTOMER.NAME>, etc. They can be created as shared mailboxes or as licensed mailboxes. If OME (Office Message Encryption) is enabled, please use licensed mailboxes.
  • All users who should be able to send securely using DPG must have the following permissions set:
    • Send on Behalf to only the secure mailboxes they should be able to send from
    • Full Access to only the secure mailboxes they should be able to access
  • A Mail-enabled Security group must be created. It is recommended to name it GRP-DPG-SecureMailboxes@<CUSTOMER.NAME>. All the secure mailboxes must be members of the group.

Mailbox requirements, recommendations and best practices

The following recommendations and best practices are supported by examples.

  • Both the system mailbox and all the secure mailboxes (funktionspostkasser) must be set with a message size limit of 150MB.

    How to set mailbox-level message size limits

    This example shows how to set mailbox-level message size limits:

    <#
    Set mailbox-level message size limits
    #>
    Set-Mailbox -Identity "dpg" -MaxSendSize 150MB -MaxReceiveSize 150MB
    <#
    Verify that mailbox message limits have been set to 150 MB
    #>
    Get-Mailbox -Identity "dpg" | Select MaxSendSize, MaxReceiveSize
    
  • The system mailbox must not be configured as resource or room mailbox.

  • Hide only the system mailbox from GAL (Hide from address lists).

    How to hide a mailbox from address lists

    This example shows how to hide a mailbox from address lists:

    <#
    Hide mailbox from address lists
    #>
    Set-Mailbox -Identity "dpg" -HiddenFromAddressListsEnabled $true
    <#
    Verify that mailbox is hidden from address lists
    #>
    Get-Mailbox -Identity "dpg" | Format-List HiddenFromAddressListsEnabled 
    
  • System mailbox language must always be set to English to prevent errors with folder names and ATP scanning.

    How to set mailbox language to en-US

    This example shows how to set mailbox language to en-US:

    <#
    Set the mailbox language
    #>
    Set-MailboxRegionalConfiguration -Identity "dpg" -Language en-US `
    -LocalizeDefaultFolderName -DateFormat "yyyy-MM-dd" -TimeFormat "HH:mm"
    <#
    Verify that the mailbox language is set
    #>
    Get-Mailbox -Identity "dpg" | Format-List Languages 
    
  • Disable retention for system mailbox, keep it to a low number of days, and exclude it from retention policies. As the system mailbox is very busy, retention can result in the system mailbox running out of space.

    How to identify if retention policy is enabled

    This example shows a way of identifying if a retention policy is enabled.
    Note: if a retention GUID exists, you must make sure to disable retention for the system mailbox – see for example "Mailbox Retention Policy", "Retention Policy" or "Inplace holds":

    <#
    See if a retention GUID exists
    #>
    Get-Mailbox -identity "dpg" | Select Name, InPlaceholds | FL 
    
  • The system mailbox must be monitored regularly by an administrator. Look for remaining read emails (indication of mail processing failure) and ensure that the mailbox does not increase in size. In general, the system mailbox should only contain mails in the short span where DPG is processing, and once DPG has successfully finished, the mails are automatically deleted from the mailbox.

  • Consider using Security Groups for managing mailbox permissions.
  • Disable https://myanalytics.microsoft.com/ for the system mailbox.

DPG SaaS integration - Azure Graph and OAuth configuration

DPG connects to Azure as a Registered Application and authenticates using OAuth 2.0. Emails are retrieved from the system mailbox using Graph, and the DPG application is only permitted access to the system mailbox.

Configuration

  1. Log in to https://portal.azure.com as an administrator, and search for Microsoft Entra ID.
  2. Go to App registrations, and select New registration. Insert a Name, set the Redirect URI to Public client/native (mobile & desktop), and set the value to urn:ietf:wg:oauth:2.0:oob. Select Register to finish:

    Register app

  3. For the App registration you just created, make a note of the Application (client) ID and the Directory (tenant) ID. Both can be found in the Overview section:

    Get IDs

  4. Go to API permissions and select Add a permission. Click Microsoft Graph, and then Application permissions.

    1. Go to the section Mail, and select Mail.ReadWrite.
    2. Go to the section User, and select User.Read.All
    3. Go to the section for GroupMember, and select GroupMember.Read.All.
    4. Select Add permissions to finish:

    Add permissions

    Add permissions

    Add permissions

    Add permissions

    Add permissions

  5. Click Grant admin consent for <customer>.

    Grant admin consent

  6. If prompted, accept the permissions requested.

    Accept permissions

  7. Make sure all application permissions are set and granted.

    Accept permissions

  8. Now select Certificates & secrets. You have two options to prove the application's identity. DPG supports both:

    • Using a certificate (recommended):

      Select Upload certificate, browse for the certificate (public key) .cer file to be used, and select Add. It is recommended to use a .cer file for an organization certificate to be installed in DPG.

      If you have access to logging in to the TIMENGO DPG portal, you can easily obtain the .cer version of an organization certificate (organisationscertifikat). Go to Certificate Check, look up the certificate, and select the button to download:

      Get certificate

      Once completed, you will see that the certificate is installed:

      Certificate installed

      Very important

      Certificates expire, so the certificate must be updated whenever it is renewed. Remember that the new 'organisationscertifikat' must also be installed in the DPG service – otherwise, DPG will fail to connect!

    • Using a secret string (NOT recommended):

      Select New client secret, name it "DPG client secret", set to for instance 24 months, and select Add:

      Secret string

      The client secret (a.k.a. application password) is shown, and it is important to copy the password:

      Secret string

  9. Use PowerShell to create a new ApplicationAccessPolicy. Update accordingly for:

    • EMAIL_ADDRESS_OF_THE_SYSTEM_MAILBOX
    • APPLICTION_CLIENT_ID
    $Desc = "Restrict DPG application to only the system mailbox"
    $DPGSystemMailbox = "EMAIL_ADRESS_OF_THE_SYSTEM_MAILBOX"
    $AppId = "APPLICATION_CLIENT_ID"
    
    New-ApplicationAccessPolicy -AppId $AppId -PolicyScopeGroupId `
    $DPGSystemMailbox -AccessRight RestrictAccess -Description $Desc  
    

    Example

    PS example

  10. Confirm that the ApplicationAccessPolicy is applied correctly by running the PowerShell below. Access to the system mailbox should say Granted, and access to a random mailbox should say Denied.

    Update accordingly for:

    • EMAIL_ADDRESS_OF_THE_SYSTEM_MAILBOX
    • EMAIL_ADDRESSS_OF_A_RANDOM_MAILBOX
    • APPLICATION_CLIENT_ID
    $DPGSystemMailbox = "EMAIL_ADRESS_OF_THE_SYSTEM_MAILBOX"
    $TestUser = "EMAIL_ADDRESS_OF_A_RANDOM_MAILBOX"
    $AppId = "APPLICATION_CLIENT_ID"
    
    Test-ApplicationAccessPolicy -Identity $DPGSystemMailbox -AppId $AppId 
    Write-host "Make sure this permission is set to Granted" `
    -ForegroundColor Green
    Pause
    Test-ApplicationAccessPolicy -Identity $TestUser -AppId $AppId 
    Write-host "Make sure this permission is set to Denied" `
    -ForegroundColor Red
    

    Example

    PS example

You have now completed the DPG SaaS integration - Azure Graph and OAuth configuration for DPG Service.

Additional configuration for DPG Outlook Office Addin: Enable Single sign-on (App registration)

DPG Outlook Office Addin authenticates with Azure as a Registered Application and authenticates using OAuth 2.0.

For more about this, see this Microsoft page.

There are two ways to register the app with Azure: You can run the PowerShell script provided below, or you can manually configure Azure by following step-by-step instructions.

This is the PowerShell script that can configure Azure for you:

Create-DPGAppRegistration script (zip file)

Or you can view it and copy it from here:

Create-DPGAppRegistration
# DPG Office Addin PowerShell Script Template
# Script to create Azure App Registration for DPG Office Add-in
# Requires Azure AD PowerShell module (Az.Resources) and Microsoft.Graph module

# Parameters
param(
    [Parameter(Mandatory=$false)]
    [string]$AppName = "DPG Office Add-in (dpgofficeaddin.dpgapi.dk)",

    [Parameter(Mandatory=$false)]
    [string]$RedirectUri = "https://dpgofficeaddin.dpgapi.dk",

    [Parameter(Mandatory=$false)]
    [string]$MicrosoftOfficeClientId = "ea5a67f6-b6f3-4338-b240-c655ddc3cc8e"
)

# Check if required modules are installed
if (-not (Get-Module -ListAvailable -Name Az.Resources)) {
    Write-Host "Azure AD PowerShell module not found. Installing..."
    Install-Module -Name Az.Resources -Scope CurrentUser -Force
}

if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) {
    Write-Host "Microsoft Graph PowerShell module not found. Installing..."
    Install-Module Microsoft.Graph -Scope CurrentUser -Force
}

# Import required modules
Import-Module Az.Resources
Import-Module Microsoft.Graph.Applications
Import-Module Microsoft.Graph.Authentication

# Disconnect from Azure AD and Microsoft Graph
Write-Host "Disconnecting from Azure AD and Microsoft Graph... So we can connect with required permissions"
Disconnect-AzAccount -ErrorAction SilentlyContinue  
Disconnect-MgGraph -ErrorAction SilentlyContinue

# Connect to Azure AD and Microsoft Graph with required permissions
try {
    Write-Host "Connecting to Azure AD and Microsoft Graph..."
    Connect-AzAccount | Out-Null
    Connect-MgGraph -Scopes "Application.ReadWrite.All", "AppRoleAssignment.ReadWrite.All", "Directory.ReadWrite.All" | Out-Null
}
catch {
    Write-Error "Failed to connect to Azure AD or Microsoft Graph: $_"
    exit 1
}

# Resolve tenant to use
$tenants = Get-AzTenant | Select-Object Id, Name, DefaultDomain

if (-not $tenants -or $tenants.Count -eq 0) {
    throw "No tenants available for the signed-in account."
}

if ($tenants.Count -gt 1) {
    Write-Host "Multiple tenants found:`n"
    for ($i = 0; $i -lt $tenants.Count; $i++) {
        $t = $tenants[$i]
        Write-Host ("[{0}] {1} ({2}) - {3}" -f ($i+1), $t.Name, $t.DefaultDomain, $t.Id)
    }

    do {
        $choice = Read-Host "Enter the number of the tenant to use"
        $parsed = 0
        $validInt = [int]::TryParse($choice, [ref]$parsed)
        $valid = $validInt -and ($parsed -ge 1) -and ($parsed -le $tenants.Count)
        if (-not $valid) { Write-Host "Invalid choice. Try again." -ForegroundColor Yellow }
    } while (-not $valid)

    $selectedTenantId = $tenants[$parsed - 1].Id
}
else {
    $selectedTenantId = $tenants[0].Id
}

# Ensure Az context is set to the selected tenant
Set-AzContext -Tenant $selectedTenantId | Out-Null

# Use this single tenant ID throughout
$tenantId = $selectedTenantId
Write-Host "Directory (tenant) ID selected: $tenantId"

# If multiple tenants were available, align Microsoft Graph connection to the selected tenant
if ($tenants.Count -gt 1) {
    Write-Host "Aligning Microsoft Graph connection to selected tenant..."
    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    Connect-MgGraph -TenantId $selectedTenantId -Scopes "Application.ReadWrite.All","AppRoleAssignment.ReadWrite.All","Directory.ReadWrite.All" | Out-Null
}

# Create the application registration
try {
    # Create the application
    Write-Host "Creating application registration..."
    $app = New-AzADApplication -DisplayName $AppName -SignInAudience "AzureADMyOrg"

    if (-not $app) {
        throw "Failed to create application"
    }

    Write-Host "Application created successfully!"
    Write-Host "Application (client) ID: $($app.AppId)"

    # Tenant ID already resolved earlier
    Write-Host "Directory (tenant) ID: $tenantId"

    # Update the application with redirect URIs

    Update-AzADApplication -ObjectId $app.Id -SPARedirectUri $RedirectUri

    # Set the Application ID URI
    Write-Host "Setting Application ID URI..."
    $appIdUri = "api://dpgofficeaddin.dpgapi.dk/$($app.AppId)"
    Update-AzADApplication -ObjectId $app.Id -IdentifierUris $appIdUri

    # Wait for the application to be available in Microsoft Graph
    Write-Host "Waiting for application to be available in Microsoft Graph..."
    $retryCount = 0
    $maxRetries = 5
    $mgApp = $null

    do {
        Start-Sleep -Seconds 5
        $mgApp = Get-MgApplication -Filter "AppId eq '$($app.AppId)'"
        $retryCount++
    } while ($null -eq $mgApp -and $retryCount -lt $maxRetries)

    if ($null -eq $mgApp) {
        throw "Failed to find application in Microsoft Graph after $maxRetries attempts"
    }

    # Create scope ID that we'll use for both the scope and pre-authorized application
    $scopeId = [guid]::NewGuid()

    # First, create the scope
    Write-Host "Adding 'Authenticated' scope..."
    $scopeParams = @{
        "api" = @{
            "oauth2PermissionScopes" = @(
                @{
                    "id" = $scopeId
                    "adminConsentDescription" = "Authenticated"
                    "adminConsentDisplayName" = "Authenticated"
                    "userConsentDescription" = "Authenticated"
                    "userConsentDisplayName" = "Authenticated"
                    "isEnabled" = $true
                    "type" = "User"
                    "value" = "Authenticated"
                }
            )
        }
    }

    Update-MgApplication -ApplicationId $mgApp.Id -BodyParameter $scopeParams

    # Then, add the pre-authorized application
    Write-Host "Pre-authorizing Microsoft Office client..."
    Start-Sleep -Seconds 5  # Wait for scope to be fully created

    $preAuthParams = @{
        "api" = @{
            "preAuthorizedApplications" = @(
                @{
                    "appId" = $MicrosoftOfficeClientId
                    "delegatedPermissionIds" = @($scopeId)
                }
            )
        }
    }

    Update-MgApplication -ApplicationId $mgApp.Id -BodyParameter $preAuthParams

    # Add Microsoft Graph permissions
    Write-Host "Adding API permissions..."
    $graphResourceId = "00000003-0000-0000-c000-000000000000" # Microsoft Graph

    $requiredResourceAccess = @{
        "requiredResourceAccess" = @(
            @{
                "resourceAppId" = $graphResourceId
                "resourceAccess" = @(
                    @{
                        "id" = "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0" # email
                        "type" = "Scope"
                    },
                    @{
                        "id" = "37f7f235-527c-4136-accd-4a02d197296e" # openid
                        "type" = "Scope"
                    },
                    @{
                        "id" = "14dad69e-099b-42c9-810b-d002981feec1" # profile
                        "type" = "Scope"
                    },
                    @{
                        "id" = "570282fd-fa5c-430d-a7fd-fc8dc98a9dca" # Mail.Read
                        "type" = "Scope"
                    },
                    @{
                        "id" = "e1fe6dd8-ba31-4d61-89e7-88639da4683d" # User.Read
                        "type" = "Scope"
                    },
                    @{
                        "id" = "e383f46e-2787-4529-855e-0e479a3ffac0" # Mail.Send
                        "type" = "Scope"
                    },
                    @{
                        "id" = "7b9103a5-4610-446b-9670-80643382c1fa" # Mail.Read.Shared
                        "type" = "Scope"
                    },
                    @{
                        "id" = "a367ab51-6b49-43bf-a716-a1fb06d2a174" # Mail.Send.Shared
                        "type" = "Scope"
                    }
                )
            }
        )
    }

    Update-MgApplication -ApplicationId $mgApp.Id -BodyParameter $requiredResourceAccess

    # Update manifest to set accessTokenAcceptedVersion to 2
    Write-Host "Setting accessTokenAcceptedVersion to 2..."
    $manifestUpdate = @{
        "api" = @{
            "requestedAccessTokenVersion" = 2
        }
    }
    Update-MgApplication -ApplicationId $mgApp.Id -BodyParameter $manifestUpdate

    # Create a service principal for the application
    Write-Host "Creating service principal..."
    $servicePrincipal = New-MgServicePrincipal -AppId $app.AppId
    Start-Sleep -Seconds 5  # Wait for service principal to be fully created

    # Get Microsoft Graph service principal
    Write-Host "Getting Microsoft Graph permissions..."
    $graphServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$graphResourceId'"

    # Grant admin consent for all required permissions
    Write-Host "Granting admin consent for permissions..."
    try {
        Write-Host "Service Principal ID: $($servicePrincipal.Id)"
        Write-Host "Graph Service Principal ID: $($graphServicePrincipal.Id)"
        New-MgOauth2PermissionGrant `
            -ClientId $servicePrincipal.Id `
            -ConsentType "AllPrincipals" `
            -ResourceId $graphServicePrincipal.Id `
            -Scope "email openid profile Mail.Read User.Read Mail.Send Mail.Read.Shared Mail.Send.Shared"
        Write-Host "Successfully granted admin consent for permissions" -ForegroundColor Green
    }
    catch {
        Write-Host "Failed to grant admin consent for permissions: $_" -ForegroundColor Red
    }

    Write-Host "App registration setup completed successfully!"
    Write-Host ""
    Write-Host "Application details for handover to Timengo DPG:"
    Write-Host "Application (client) ID: $($app.AppId)"
    Write-Host "Directory (tenant) ID: $tenantId"

    $cwd = Split-Path -Path $PSCommandPath -Parent
    Set-Content -Path $cwd\DPGOfficeAddin-TechnicalDetails.txt -Value "Application (client) ID: $($app.AppId)`nDirectory (tenant) ID: $tenantId"
    write-host "Details saved to $cwd\DPGOfficeAddin-TechnicalDetails.txt"

    # Disconnect from Azure AD and Microsoft Graph
    Write-Host "Disconnecting from Azure AD and Microsoft Graph..."
    Disconnect-AzAccount -ErrorAction SilentlyContinue | Out-Null
    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
}
catch {
    Write-Error "Failed to create app registration: $_"
    exit 1
}

Step-by-step instructions

Following the steps to manually configure Azure helps you to understand the configuration. The script provided above is an automated version of the steps below.

Complete the following steps to configure the App registration in Azure:

  1. Log in to Azure as an administrator, and go to Microsoft Entra ID.
  2. Select App registrations, and select New registration:
    App registrations

  3. Specify details for the app registration

    • Set Name as DPG Office Add-in (dpgofficeaddin.dpgapi.dk)
    • Select Supported account types as Accounts in this organization directory only (Single tenant)
    • Set Redirect URI to Single-page application (SPA), and set the value to https://dpgofficeaddin.dpgapi.dk
    • Finish by selecting Register.

    New registration

  4. Make a note of the Application (client) ID and Directory (tenant) ID from the Overview section as highlighted below.
    Get IDs

  5. Select Expose an API, and select Add to create an Application ID URI.

  6. Update the Application ID URI to api://dpgofficeaddin.dpgapi.dk/<Application (client) ID>, and finish by selecting Save:
    Update ID URI

  7. Now select Add a scope in the Expose an API pane, and insert the text Authenticated as shown below. Make sure to select Admin and users in the Who can consent? field, and Enabled in the State field. Finish by selecting Add scope:
    Add scope

  8. Still in the Expose an API pane, select Add a client application, and insert ea5a67f6-b6f3-4338-b240-c655ddc3cc8e, which pre-authorizes all Microsoft Office Application endpoints. Also select the Authorized scopes box, and finish by selecting Add application:
    Add application

  9. Open the API permissions pane, and select Add a permission:
    Add permission

  10. Select Microsoft Graph:
    MS Graph

  11. Select Delegated permissions:
    MS Graph permissions

  12. Select permissions for email, openid, profile, Mail.Read, Mail.Send, Mail.Read.Shared, Mail.Send.Shared, and User.Read. Finish by selecting Add permissions:
    MS Graph permissions
    MS Graph permissions
    MS Graph permissions
    MS Graph permissions

  13. In the Configured permissions pane, select Grant admin consent for…, and finish by selecting Yes.
    MS Graph permissions

  14. Once permissions have been granted, the Configured permissions view should look like this:
    MS Graph permissions

  15. Select Manifest, and change the value for "accessTokenAcceptedVersion" from null to 2, and select Save to finish.
    Manifest

  16. Select Authentication for the app registration you have created. In the section Single-page application Redirect URIs, select Add URI, and add the value brk-multihub://dpgofficeaddin.dpgapi.dk. Select Save to finish.
    Authentication

You have now completed Enable Single sign-on (App registration) configuration for DPG Outlook Office Addin.

Handover to Timengo DPG

After completing the configuration steps, you must hand over the following details to Timengo DPG as specified in the welcome letter: