Senast uppdaterad: 2025-08-08

Skicka e-post från Opter med Microsoft 365

Gäller från och med Opter 2025.06.00.

Om ni använder Microsoft 365 som e-posttjänst måste ni autentisera Opter genom att registrera programmet som en app i Azure.

Appregistreringen i Azure kan göras med ett PowerShell-skript som finns som kodexempel nedan. Anledningen till att skriptet inte kan köras i samband med en uppdatering är att den som kör det måste ha skrivrättigheter för Azure AD och vara Microsoft 365-administratör för er organisation, vilket inte Opter är. Skriptet gör de inställningar som behövs i Azure och Exchange. När skriptet har körts behöver några uppgifter läggas in på fliken Servrar i kontorsinställningarna.

Steg 1: Ta reda på era Microsoft 365-uppgifter

När skriptet körs kommer du att behöva ange följande fyra uppgifter. Börja med att ta reda på dem så att du har dem till hands, förslagsvis i en textfil så att du bara kan kopiera in dem när det är dags.

Du måste vara administratör för Azure och Microsoft 365 i er organisation för att kunna ta reda på uppgifterna och köra skriptet.

  1. Azure Tenant ID

    Logga in på portal.azure.com och sök på ”klientorganisation” eller ”tenant properties”. Kopiera sedan värdet för Klientorganisations-ID.

  2. Användarnamn för ett adminkonto för Microsoft 365

    Vanligtvis en e-postadress. Eftersom du är administratör kan du troligen använda din egen adress. Adressen används endast för att skriptet ska kunna göra inställningar i Microsoft 365/Exchange Admin.

  3. Microsoft 365-domänen

    Vanligtvis samma domän som i e-postadressen till adminkontot.

  4. E-postadress som idag används som primär avsändare

    Det här är den e-postadress som primärt används för att skicka e-post från Opter. Den finns i fältet E-post på fliken Allmänt i kontorsinställningarna i Opter.

    Systemägaren för Opter vet vilka avsändaradresser som används av Opter.

    Det går att skicka till exempel fakturor från en annan avsändaradress (fältet E-post på fliken Fakturadetaljer i kontorsinställningarna), men den måste läggas in i Microsoft 365 i efterhand. Det går bara att lägga till en e-postadress med skriptet. Mer information finns i Alternativa avsändaradresser nedan.

Steg 2: Öppna Windows Powershell

Sök på ”windows powershell” på Start-menyn i Windows och öppna Windows PowerShell, inte Windows PowerShell ISE.

En kommandotolk öppnas. Sätt rättigheten RemoteSigned genom att klistra in nedanstående på kommandoraden och trycka på Enter. Mer information om rättigheterna finns på Microsofts webbplats.

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

När du har kört skriptet kan du ställa tillbaka rättigheterna med kommandot Set-ExecutionPolicy Restricted -Scope CurrentUser.

Steg 3: Kör skriptet

Kopiera och spara nedanstående skript som en PS1-fil på datorn. Kör det sedan från Windows PowerShell genom att klistra in sökvägen till filen på kommandoraden och trycka på Enter.

Skriptet använder specifika versioner av vissa komponenter. Det kan hända att komponenter som du redan har installerade måste avinstalleras först (hjälpkod finns i skriptet).

Eftersom skriptet tvingar in specifika versioner av komponenterna kan det hända att det ”förstör” den befintliga miljön i PowerShell och att den behöver återställas manuellt. Om du har en PowerShell-miljö som används i produktion och inte bör störas kan du köra skriptet på en virtuell dator.

#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 kommer att få en fråga om du vill installera modulerna som du måste svara ja på.

När du får frågorna om att ange de uppgifter som du tog reda på i steg 1, till exempel Azure tenant ID, klistrar du in dem och trycker på Enter så fortsätter skriptet att köras.

Vad gör skriptet?

Vi bryter ner det i block.

  • Rad 20–70 installerar MS Graph-modulerna för PowerShell. De behövs för att resten av skriptet ska fungera. Modulerna läggs i C:\Program Files\WindowsPowerShell\Modules\.

  • Rad 72–77 loggar in på Azure AD.

  • Rad 80–119 registrerar Opter som app i Azure med namnet ”OpterMessageProxy” och ger den appen rättighet att skicka e-post. Du kan byta namnet ”OpterMessageProxy” till något annat på rad 81, men det är enklare att behålla standardnamnet om ni behöver kontakta supporten i något ärende.

  • Rad 132–142 lägger in OpterMessageProxy i en behörighetsgrupp i Exchange med begränsade rättigheter.

  • Rad 144–224 visar den info som måste läggas in i kontorsinställningarna i Opter (fliken Servrar). Informationen kopieras till Urklipp, men kan även kopieras från den dialogruta som öppnas. Stäng inte dialogrutan innan du har lagt in informationen i kontorsinställningarna. Eller klistra in informationen i en textfil, eller någon annanstans, som du sedan kan kopiera från.

    Klienthemligheten går inte att hämta eller återställa. Om du inte klistrar in den någonstans måste du skapa en ny i Azure-portalen. De andra uppgifterna kan hämtas från Azure-portalen i efterhand om det skulle behövas.

Steg 4: Koppla ihop Opter med Microsoft 365/MS Graph

Klicka på Inställningar > Kontor > fliken Servrar och fyll i följande fält med den information som du klistrade in i textfilen:

  • Ms Graph Tenant Id

  • Ms Graph Client Id

  • Ms Graph Client Secret

Klicka sedan på Testa e-post, ange dig själv som mottagare och kontrollera att du får ett meddelande.

Om det finns ett värde i fältet Ms Graph Tenant Id spelar det ingen roll om fälten ovanför på den här fliken är ifyllda (SMTP-server, Port, Användarnamn, Lösenord och Säker överföring (SSL)). De uppgifterna används inte, autentiseringen sker genom appregistreringen i Azure. Det kan vara bra att ta bort informationen i dem så att det inte sker några missförstånd.

Steg 5: Ge appen OpterMessageProxy administratörsbehörighet

Logga in på portal.azure.com och sök på ”OpterMessageProxy”. Gå till API-behörigheter i menyn till vänster och klicka på Bevilja administratörsbehörighet för {0}

Appregistreringen i Azure

Du hittar appregistreringen på portal.azure.com genom att söka på ”optermessageproxy” och välja det sökresultat som är märkt Program. Du kan också gå till appregistreringarna och leta i listan över registrerade program.

Under OpterMessageProxy kan du se och kopiera Ms Graph Tenant Id = Katalog-ID (klientorganisation) (2) och Ms Graph Client Id = Program-ID (klient) (1).

Ms Graph Client Secret kan inte visas eller återställas i Azure-portalen. Om du missade att klistra in det efter att skriptet kördes måste du skapa en ny hemlighet. Gå till Hantera (1) > Certifikat och hemligheter (2) i menyn till vänster och klicka på Ny klienthemlighet. Där kan du även kontrollera när klienthemligheten upphör att gälla.

Flera domäner

Om ni har flera domäner, till exempel ”budfirman.centraleurope.se” och ”budfirman.nordics.se”, måste ni köra skriptet en gång för varje domän, och ändra namnet på appregistreringen varje gång ni kör det. Om inte namnet ändras skrivs den föregående appregistreringen över när skriptet körs igen. Gör så här:

  1. Ändra rad 81 i skriptet så att namnet identifierar den första domänen. I Budfirmans fall ändras rad 81 från:

    $appName = "OpterMessageProxy"

    till

    $appName = "OpterMessageProxy_centraleurope"

  2. Spara och kör skriptet.

  3. Ändra rad 81 till namnet på nästa domän. I Budfirman fall från:

    $appName = "OpterMessageProxy_centraleurope"

    till

    $appName = "OpterMessageProxy_nordics"

  4. Spara och kör skriptet igen.

Nu finns det två appregistreringar i Azure som kan hanteras separat.

Alternativa avsändaradresser

Om du vill skicka till exempel fakturor från Opter med en annan adress, till exempel invoice@budfirman.se, lägger du till de alternativa adresserna i säkerhetsgruppen OpterMessageProxy_group i Exchange. Om ni ändrade namn när ni registrerade Opter i Azure heter gruppen det namn ni angav + ”_group”. Alla avsändaradresser ni har lagt till i Opter måste finnas med som medlemmar i säkerhetsgruppen.

Mer information och exempel på hur ni lägger till olika avsändaradresser finns i Alternativa e-postadresser.

Så här lägger du till en avsändaradress som medlem i gruppen OpterMessageProxy_group i Exchange admin center. Mer information finns på Microsofts webbplats.

Se även