Senest opdateret: 2025-10-29

Send e-mails fra Opter med Microsoft 365

Gælder fra og med Opter 2025.06.00.

Hvis du bruger Microsoft 365 som din e-mailtjeneste, skal du godkende Opter ved at registrere programmet som en app i Azure.

Appregistreringen i Azure kan udføres med et PowerShell-script, som findes som kodeeksempel nedenfor. Grunden til, at scriptet ikke kan køres i forbindelse med en opdatering, er, at den person, der kører det, skal have tilladelse til at ændre Microsoft Entra ID (tidligere Azure Active Directory) og være Microsoft 365-administrator for din organisation, hvilket Opter ikke er. Scriptet foretager de nødvendige indstillinger i Entra ID og Exchange. Når scriptet er kørt, skal nogle oplysninger indtastes under fanen Servere i kontorindstillingerne.

Trin 1: Find dine Microsoft 365-oplysninger

Når scriptet kører, skal du angive følgende fire oplysninger. Start med at finde dem, så du har dem klar – f.eks. i en tekstfil, så du bare kan kopiere dem ind, når det bliver nødvendigt.

Du skal være administrator af Microsoft Entra ID og Microsoft 365 i din organisation for at kunne hente data og køre scriptet.

  1. Azure Tenant ID

    Log ind på portal.azure.com og søg efter "klientorganisation" eller "tenant properties". Kopier derefter værdien for Tenant ID.

  2. Brugernavn for en administratorkonto til Microsoft 365

    Typisk en e-mailadresse. Da du er administrator, kan du sandsynligvis bruge din egen adresse. Adressen bruges kun til, at scriptet kan foretage indstillinger i Microsoft 365/Exchange Admin.

  3. Microsoft 365-domæne

    Typisk det samme domæne som i e-mailadressen til administratorkontoen.

  4. E-mailadresse, der i øjeblikket bruges som primær afsender

    Dette er den e-mailadresse, der primært bruges til at sende e-mails fra Opter. Den findes i feltet Email på fanen Generelt i kontorindstillingerne i Opter.

    Ejeren af Opter-systemet ved, hvilke afsenderadresser der bruges af Opter.

    Det er f.eks. muligt at sende fakturaer fra en anden afsenderadresse (feltet Email på fanen Fakturadetaljer i kontorindstillingerne), men den skal tilføjes til Microsoft 365 bagefter. Du kan kun tilføje en e-mailadresse ved hjælp af scriptet. Du kan finde yderligere oplysninger i Alternative afsenderadresser nedenfor.

Trin 2: Åbn Windows Powershell

Søg efter "windows powershell" på Start-menuen i Windows, og åbn Windows PowerShell, ikke Windows PowerShell ISE.

Et kommandovindue åbnes. Indstil rettigheden RemoteSigned ved at indsætte nedenstående kommando i kommandolinjen og trykke på Enter. Du kan finde flere oplysninger om rettighederne på Microsofts hjemmeside.

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

Når du har kørt scriptet, kan du gendanne rettighederne med kommandoen Set-ExecutionPolicy Restricted -Scope CurrentUser.

Trin 3: Kør scriptet

Kopier og gem nedenstående script som en PS1-fil på computeren. Kør det derefter fra Windows PowerShell ved at indsætte stien til filen i kommandolinjen og trykke på Enter.

Scriptet bruger specifikke versioner af visse komponenter. Det kan være nødvendigt at afinstallere komponenter, du allerede har installeret, først (hjælpekode findes i scriptet).

Fordi scriptet tvinger bestemte versioner ind, kan det "ødelægge" det eksisterende PowerShell-miljø, som derefter muligvis skal gendannes manuelt. Hvis du har et PowerShell-miljø, der anvendes i produktion og ikke bør ødelægges, bør du i stedet køre scriptet på en virtuel maskine.

#Run the following command manually in Windows PowerShell before running the script below: Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser


#Script to create app registration in Azure with Mail.Send rights for e-mail addresses in Mail-Enabled secutity group created.
#Application secret is set to expire after 999 years. This can be changed to the desired value by modifying $AppYears value
#Please note that tenant wide admin consent must be granted manually after this script has been executed.
#For Opter AB, 2025-04-24, David Magnusson Rosén
#
#Change log
#------------------------------------------------------------------------------------------------------------------------------
#202
#
#


#Code to uninstall entire module with sub-modules if needed 
#foreach ($module in (get-module -listavailable ModuleName*).name |get-unique) {write-host "removing module $module" uninstall-module $module }


#Install/import required modules
echo "Installing/importing required modules (can take several minutes)"

echo "Installing prerequisites..."
#PowerShellGet
if (Get-Module -ListAvailable -Name PowerShellGet) 
{
    echo "Updating module PowerShellGet..."
    Update-Module -Name PowerShellGet -Force

else 
{
    echo "Installing module PowerShellGet..."
    Install-Module -Name PowerShellGet -Scope CurrentUser -Force
}

echo "Installing module Az.Accounts..."
Install-Module -Name Az.Accounts -Scope CurrentUser -RequiredVersion 2.12.1 -AllowClobber -Repository PSGallery -Force
echo "Importing relevant functions from module Az.Accounts..."
Import-Module Az.Accounts -RequiredVersion 2.12.1

echo "Installing module Az.Resources..."
Install-Module -Name Az.Resources -Scope CurrentUser -RequiredVersion 6.6.0 -AllowClobber -Repository PSGallery -Force
echo "Importing relevant functions from module Az.Resources..."
Import-Module Az.Resources -RequiredVersion 6.6.0

echo "Installing module AzureAD..."
Install-Module AzureAD
echo "Importing relevant functions from module AzureAD..."
Import-Module AzureAD -Function Connect-AzureAD, Get-AzureADApplication, New-AzureADApplication, New-AzureADServicePrincipal, Add-AzADAppPermission, Get-AzADServicePrincipal, Get-AzADServicePrincipal_List

echo "Installing module Microsoft.Graph.Authentication..."
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Repository PSGallery -AllowClobber -Force -requiredversion 1.27
echo "Importing relevant functions from module Microsoft.Graph.Authentication..."
Import-Module Microsoft.Graph.Authentication -Function Connect-MgGraph 
echo ""

echo "Installing module Microsoft.Graph.Applications..."
Install-Module Microsoft.Graph.Applications -Scope CurrentUser -Repository PSGallery -AllowClobber -Force
echo "Importing relevant functions from module Microsoft.Graph.Applications..."
Import-Module Microsoft.Graph.Applications -Function Add-MgApplicationPassword
echo ""

echo "Installing module ExchangeOnlineManagement..."
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser -RequiredVersion 3.6.0 -Repository PSGallery  -AllowClobber  -Force
echo "Importing module ExchangeOnlineManagement..."
Import-Module ExchangeOnlineManagement -RequiredVersion 3.6.0 -Function Connect-ExchangeOnline, New-DistributionGroup, New-ApplicationAccessPolicy, Add-DistributionGroupMember
echo ""

echo "Necessary modules/functions installed/imported!"
echo ""

#Query for tenant Id
$TenantId = Read-Host "Please provide your Azure Tenant Id"

#Connect to AzureAD 
Connect-AzureAD -TenantId $TenantId
Connect-AzAccount


#Create app registration and service principal
$appName = "OpterMessageProxy"
if(!($myApp = Get-AzureADApplication -Filter "DisplayName eq '$($appName)'"  -ErrorAction SilentlyContinue))
{
    $myApp = New-AzureADApplication -DisplayName $appName    
    $myServicePrincipal = New-AzureADServicePrincipal -AccountEnabled $true -AppId $myApp.AppId -AppRoleAssignmentRequired $true -DisplayName $myApp.DisplayName
    $AppId = $myApp.AppId
}

#Wait for app registration to complete
echo "Wait for app registration to complete..."
Start-Sleep -Seconds 15

#Get service principal for Ms Graph
$graphApiId = '00000003-0000-0000-c000-000000000000'
$mailSendId = 'b633e1c5-b582-4048-a93e-9f11b44c7e96'
$graphSp = Get-AzADServicePrincipal -Filter "appId eq '$graphApiId'"

#Connect to MsGraph
Connect-MgGraph -Scopes "Application.ReadWrite.All", "DelegatedPermissionGrant.ReadWrite.All" 

#Add Mail.Send rights to app registration
Add-AzADAppPermission -ObjectId $myApp.ObjectId -ApiId $graphApiId -PermissionId $mailSendId -Type Role

#Wait for rights assignment to complete
Start-Sleep -Seconds 15

#Create a secret for the app registration
$AppYears = "999"

$PasswordCred = @{ 
    displayName = "Secret_${appName}_unlimited"
    endDateTime = (Get-Date).AddYears($AppYears)
}

# Add App Client Secret - Valid for 999 years
$Secret = Add-MgApplicationPassword -ApplicationId $myApp.ObjectId -PasswordCredential $PasswordCred
$SecretText = $Secret.SecretText 
# Write Client Secret value
echo "Secret: ${SecretText}" 

#For future use
#Grant tenant-wide consent for app registration
# Get-MgServicePrincipal -Filter "displayName eq 'Microsoft Graph'" -Property AppRoles | Select -ExpandProperty appRoles |fl
# $params = @{
  # "PrincipalId" ="aaaaaaaa-bbbb-cccc-1111-222222222222"
  # "ResourceId" = "a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1"
  # "AppRoleId" = "df021288-bdef-4463-88db-98f22de89214"
# }
# New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId 'aaaaaaaa-bbbb-cccc-1111-222222222222' -BodyParameter $params | 
#  Format-List Id, AppRoleId, CreatedDateTime, PrincipalDisplayName, PrincipalId, PrincipalType, ResourceDisplayName

#Connect to Exchange Online
$UserPrincipleName = Read-Host "Please provide your Microsoft365 admin user (usually yourname@yourdomain.com)"
Connect-ExchangeOnline -UserPrincipalName $UserPrincipleName

#Set up mail sending restrictions
$UserDomain = Read-Host "Please provide your Microsoft365 domain(usually yourdomain.com)"
$SenderAddress = Read-Host "Please provide an existing e-mail address to be used as sender"
$GroupName = "${appName}_group"
$mesGroup = New-DistributionGroup -Name $GroupName -PrimarySmtpAddress "${appName}@${UserDomain}" -Alias $GroupName.ToLower() -Type security 
$accessPolicy = New-ApplicationAccessPolicy -AppId $myApp.AppId -PolicyScopeGroupId "${appName}@${UserDomain}" -AccessRight RestrictAccess -Description "Restrict this app to members of distribution group ${GroupName}."
$dgMember = Add-DistributionGroupMember -Identity $GroupName -Member $SenderAddress -Confirm:$false

#Show settings to use in Opter office settings and copy them to clipboard
#$ButtonType = [System.Windows.Forms.MessageBoxButtons]::OK
$MessageIcon = [System.Windows.Forms.MessageBoxIcon]::Information
$MessageBody =  "Ms Graph Tenant Id:     ${TenantId} `n"
$MessageBody += "Ms Graph Client Id:     ${AppId} `n"
$MessageBody += "Ms Graph Client Secret: ${SecretText} `n"
$MessageInfo = "`n`n"
$MessageInfo += "NB!`nThe client secret disappears when you close this message and cannot be retreived or restored.`n"
$MessageInfo += "If you don't make a note of it, you have to create a new one manually in the Azure portal!`n`n"
$MessageInfo += "The above settings have been copied to the clipboard. Enter them in the office settings in Opter."
$MessageTitle = "Info to be used in Opter office settings"

Set-Clipboard -Value $MessageBody
#[System.Windows.Forms.MessageBox]::Show("${MessageBody} ${MessageInfo}",$MessageTitle,$ButtonType,$MessageIcon)


#Custom result form
$color = [System.Drawing.Color]::White
$form = New-Object System.Windows.Forms.Form
$form.Text = $MessageTitle
$form.BackColor = $color
$form.Width = 800  
$form.Height = 275

$lblTenantId = New-Object System.Windows.Forms.Label
$lblTenantId.Text = "Ms Graph Tenant Id:"
$lblTenantId.AutoSize = $true
$lblTenantId.Font = New-Object System.Drawing.Font("Courier", 10
$lblTenantId.Location = New-Object System.Drawing.Point(10,20)

$txtTenantId = New-Object System.Windows.Forms.TextBox
$txtTenantId.Height = 20
$txtTenantId.Width = 375
$txtTenantId.Multiline = $true
$txtTenantId.Text = $TenantId
$txtTenantId.Font = New-Object System.Drawing.Font("Courier New", 10
$txtTenantId.Location = New-Object System.Drawing.Point(200,20)

$lblClientId = New-Object System.Windows.Forms.Label
$lblClientId.Text = "Ms Graph Client Id:"
$lblClientId.AutoSize = $true
$lblClientId.Font = New-Object System.Drawing.Font("Courier", 10
$lblClientId.Location = New-Object System.Drawing.Point(10,50)

$txtClientId = New-Object System.Windows.Forms.TextBox
$txtClientId.Height = 20
$txtClientId.Width = 375
$txtClientId.Multiline = $true
$txtClientId.Text = $AppId
$txtClientId.Font = New-Object System.Drawing.Font("Courier New", 10
$txtClientId.Location = New-Object System.Drawing.Point(200,50)

$lblClientSecret = New-Object System.Windows.Forms.Label
$lblClientSecret.Text = "Ms Graph Client Secret:"
$lblClientSecret.AutoSize = $true
$lblClientSecret.Font = New-Object System.Drawing.Font("Courier", 10
$lblClientSecret.Location = New-Object System.Drawing.Point(10,80)

$txtClientSecret = New-Object System.Windows.Forms.TextBox
$txtClientSecret.Height = 20
$txtClientSecret.Width = 375
$txtClientSecret.Multiline = $true
$txtClientSecret.Text = $SecretText
$txtClientSecret.Font = New-Object System.Drawing.Font("Courier New", 10
$txtClientSecret.Location = New-Object System.Drawing.Point(200,80)

$lblMessage = New-Object System.Windows.Forms.Label
$lblMessage.Text = $MessageInfo
$lblMessage.AutoSize = $true
$lblMessage.Font = New-Object System.Drawing.Font("Courier", 10
$lblMessage.Location = New-Object System.Drawing.Point(10,90)

$form.Controls.Add($lblTenantId)
$form.Controls.Add($txtTenantId)
$form.Controls.Add($lblClientId)
$form.Controls.Add($txtClientId)
$form.Controls.Add($lblClientSecret)
$form.Controls.Add($txtClientSecret)
$form.Controls.Add($lblMessage)

$form.ShowDialog()

Du vil blive spurgt, om du vil installere modulerne – her skal du svare ja.

Når du bliver bedt om at indtaste de oplysninger, som du fandt frem til i trin 1 (f.eks. Azure tenant ID), skal du indsætte dem og trykke på Enter, hvorefter scriptet fortsætter.

Hvad gør scriptet?

Vi deler det op i blokke.

  • Linje 20-70 installerer MS Graph-modulerne til PowerShell. De er nødvendige for, at resten af manuskriptet fungerer. Modulerne installeres i C:\Program Files\WindowsPowerShell\Modules\.

  • Linje 72-77 logger ind på Microsoft Entra ID (tidligere Azure Active Directory).

  • Linje 80-119 registrerer Opter som en app i Azure med navnet "OpterMessageProxy" og giver denne app tilladelse til at sende e-mails. Du kan ændre navnet "OpterMessageProxy" i linje 81, men det er nemmere at beholde standardnavnet, hvis du får brug for at kontakte support.

  • Linje 132-142 tilføjer OpterMessageProxy til en tilladelsesgruppe i Exchange med begrænsede tilladelser.

  • Linje 144-224 viser de oplysninger, der skal indtastes i kontorindstillingerne i Opter (fanen Servere). Oplysningerne kopieres til udklipsholderen, men kan også kopieres fra den dialogboks, der åbnes. Luk ikke dialogboksen, før oplysningerne er blevet indtastet i kontorindstillingerne. Alternativt kan du indsætte dem i en tekstfil eller et andet sted, hvorfra du senere kan kopiere dem.

    Klienthemmeligheden kan ikke hentes eller gendannes. Hvis du ikke har gemt den et sted, skal du oprette en ny i Azure-portalen. De øvrige oplysninger kan hentes fra Azure-portalen bagefter, hvis det bliver nødvendigt.

Trin 4: Forbind Opter med Microsoft 365/MS Graph

Klik på Indstillinger > Kontor > Servere og udfyld følgende felter med de oplysninger, du har indsat i tekstfilen:

  • Ms Graph Tenant Id

  • Ms Graph Client Id

  • Ms Graph Client Secret

Klik derefter på Test email, angiv dig selv som modtager, og tjek, at du modtager en besked.

Hvis der findes en værdi i feltet Ms Graph Tenant Id, er det ligegyldigt, om felterne ovenfor under denne fane er udfyldt (SMTP-server, Port, Brugernavn, Adgangskode og Sikke forbindelse (SSL)). Disse oplysninger bruges ikke, da autentificeringen sker via appregistreringen i Azure. Det kan være en god idé at fjerne oplysningerne i dem for at undgå misforståelser.

Trin 5: Giv appen OpterMessageProxy administratorrettigheder

Log ind på portal.azure.com, og søg efter "OpterMessageProxy". Gå til API permissions i menuen til venstre, og klik på Grant admin consent for {0}

Appregistrering i Azure

Du kan finde appregistreringen på portal.azure.com ved at søge på "optermessageproxy" og vælge det søgeresultat, der hedder Application. Du kan også gå til appregistreringer og lede i listen over registrerede programmer.

Under OpterMessageProxy kan du se og kopiere Ms Graph Tenant Id = Directory (tenant) ID (2) og Ms Graph Client Id = Application (client) ID (1).

Ms Graph Client Secret kan ikke vises eller gendannes i Azure-portalen. Hvis du glemte at kopiere den efter at have kørt scriptet, skal du oprette en ny hemmelighed. Gå til Manage (1) > Certificates & secrets (2) i menuen til venstre, og klik på New client secret. Der kan du også tjekke, hvornår klienthemmeligheden udløber.

Flere domæner

Hvis du har flere domæner, f.eks. "budfirman.centraleurope.se" og "budfirman.nordics.se", skal du køre scriptet én gang for hvert domæne og ændre navnet på app-registreringen, hver gang du kører det. Medmindre navnet ændres, overskrives den tidligere app-registrering, når scriptet køres igen. Sådan gør du:

  1. Ændr linje 81 i scriptet, så navnet identificerer det første domæne. I tilfælde af kurerfirmaet ændres linje 81 fra:

    $appName = "OpterMessageProxy"

    til

    $appName = "OpterMessageProxy_centraleurope"

  2. Gem og kør scriptet.

  3. Skift linje 81 til navnet på det næste domæne. I Budfirman-sagen fra:

    $appName = "OpterMessageProxy_centraleurope"

    til

    $appName = "OpterMessageProxy_nordics"

  4. Gem og kør scriptet igen.

Nu er der to app-registreringer i Azure, som kan administreres hver for sig.

Alternative afsenderadresser

Hvis du f.eks. ønsker at sende fakturaer fra Opter med en anden adresse, f.eks. invoice@budfirman.se, skal du tilføje disse alternative adresser til sikkerhedsgruppen OpterMessageProxy_group i Exchange. Hvis du ændrede navnet under registreringen af Opter i Azure, vil gruppen få det navn, du angav, efterfulgt af "_group". Alle de afsenderadresser, du har tilføjet til Opter, skal være inkluderet som medlemmer af sikkerhedsgruppen.

Flere oplysninger og eksempler på, hvordan man tilføjer forskellige afsenderadresser, findes i Alternative e-mailadresser.

Sådan tilføjer du en afsenderadresse som medlem af OpterMessageProxy_group i Exchange admin center. Du kan finde flere oplysninger på Microsofts hjemmeside.

Se også