11. juli 2026 5 min lesetid

Få tilgang til Exchange Online med PowerShell: Erstatt EWS med Microsoft Graph

EWS avsluttes i Exchange Online 1. oktober 2026. Slik registrerer du en app, autentiserer et PowerShell-skript med sertifikat, begrenser tilgangen til enkelte postbokser og behandler meldinger og vedlegg via Microsoft Graph.

Microsoft avvikler Exchange Web Services (EWS) i Exchange Online 1. oktober 2026. Skript som henter meldinger eller vedlegg fra en postboks, må derfor bytte til Microsoft Graph.

Eksempelet i dette innlegget kjører uten brukerinnlogging: Det laster ned ZIP-vedlegg fra en postboks, pakker dem ut, flytter behandlede meldinger og sender deretter en rapport. Dette krever en appregistrering, et sertifikat og to Graph-tillatelser. Verdier som example.com, tenant-ID og app-ID er plassholdere.

1. Nødvendige PowerShell-moduler

Tre moduler fra Microsoft Graph SDK er tilstrekkelig, ikke hele metamodulen Microsoft.Graph:

Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Mail, Microsoft.Graph.Users.Actions -Scope AllUsers
Forklaring av alternativer
AlternativEffekt
Microsoft.Graph.Authentication, Microsoft.Graph.Mail, Microsoft.Graph.Users.ActionsDe tre nødvendige delmodulene som posisjonsargument for -Name: innlogging, e-post-cmdleter og handlinger som Send-MgUserMail
-Scope AllUsersInstallerer modulene systemomfattende under Program Files; nødvendig for at de også skal være tilgjengelige for tjenestekontoen til den senere konfigurerte Scheduled Task (krever administratorrettigheter)

2. Appregistrering i Entra ID

Et uovervåket skript logger på som en applikasjon med egne rettigheter. Opprett en ny registrering i Entra Admin Center under App registrations, og tildel disse to rettighetene under API permissions → Microsoft Graph → Application permissions:

  • Mail.ReadWrite: lese e-post og flytte den etter behandling

  • Mail.Send: sende rapport-e-posten

Gi deretter administratortillatelse, og noter tenant-ID-en og Application (Client) ID-en.

3. Sertifikat i stedet for Client Secret

En app-only-innlogging fungerer med Client Secret eller sertifikat. For planlagte oppgaver er et sertifikat det beste valget: Den private nøkkelen forblir i sertifikatlageret, og skriptet inneholder ikke noe passord. Opprett sertifikatet på serveren som skal kjøre skriptet, og eksporter bare den offentlige delen:

$cert = New-SelfSignedCertificate -Subject "CN=eCall-Graph" `
    -CertStoreLocation "Cert:\LocalMachine\My" `
    -KeyExportPolicy NonExportable -KeySpec Signature `
    -KeyLength 2048 -NotAfter (Get-Date).AddYears(2)

Export-Certificate -Cert $cert -FilePath .\eCall-Graph.cer
$cert.Thumbprint   # -> im Skript als Thumbprint verwenden
Forklaring av alternativer
AlternativEffekt
New-SelfSignedCertificate -SubjectSertifikatets emnenavn; brukes bare for gjenkjennelse i sertifikatlageret
-CertStoreLocation "Cert:\LocalMachine\My"Lagrer sertifikatet i datamaskinlageret, ikke brukerens lager; dermed er det tilgjengelig uavhengig av innlogget bruker
-KeyExportPolicy NonExportableHindrer eksport av den private nøkkelen; den forlater ikke serveren
-KeySpec SignatureOppretter en signeringsnøkkel; appinnloggingen bruker den til å signere token-request-assertion
-KeyLength 2048RSA-nøkkellengde på 2048 bit
-NotAfter (Get-Date).AddYears(2)Utløper om to år; sertifikatet må deretter fornyes og lastes opp på nytt
Export-Certificate -CertSertifikatobjektet som skal eksporteres
-FilePathMålfil; inneholder som .cer bare den offentlige delen

Last opp den eksporterte .cer-filen i appregistreringen under Certificates & secrets. Kontoen til Scheduled Task trenger lesetilgang til den private nøkkelen (certlm.msc → sertifikat → All Tasks → Manage Private Keys).

4. Begrens tilgang til enkelte postbokser

Application Permissions gjelder først for alle postbokser i tenanten. Begrens derfor appen med en Application Access Policy til en e-postaktivert sikkerhetsgruppe. Dette trinnet utføres én gang i Exchange Online PowerShell:

New-ApplicationAccessPolicy -AppId "<App-ID>" `
    -PolicyScopeGroupId "graph-mailboxes@example.com" `
    -AccessRight RestrictAccess `
    -Description "eCall Graph: nur Log-Postfach"

# Kontroller at policyen gjelder
Test-ApplicationAccessPolicy -AppId "<App-ID>" -Identity "ecall-logs@example.com"
Forklaring av alternativer
AlternativEffekt
New-ApplicationAccessPolicy -AppIdApplication (Client) ID for appregistreringen som policyen gjelder for
-PolicyScopeGroupIdE-postaktivert sikkerhetsgruppe der medlemmene definerer virkeområdet
-AccessRight RestrictAccessBegrenser appen til gruppens postbokser; alternativet DenyAccess ville sperret nettopp disse postboksene
-DescriptionFritekst for dokumentasjon av policyen
Test-ApplicationAccessPolicy -IdentityKontrollerer for en bestemt postboks om appen har tilgang til den (AccessCheckResult: Granted eller Denied)

5. Opprett forbindelse

Innloggingen bruker tenant-ID, app-ID og sertifikatets thumbprint, helt uten brukerinteraksjon:

$TenantId   = "00000000-0000-0000-0000-000000000000"
$ClientId   = "00000000-0000-0000-0000-000000000000"
$Thumbprint = "0000000000000000000000000000000000000000"
$Mailbox    = "ecall-logs@example.com"

Import-Module Microsoft.Graph.Authentication, Microsoft.Graph.Mail, Microsoft.Graph.Users.Actions
Connect-MgGraph -TenantId $TenantId -ClientId $ClientId `
    -CertificateThumbprint $Thumbprint -NoWelcome
Forklaring av alternativer
AlternativEffekt
Connect-MgGraph -TenantIdTenant som appen logger på mot
-ClientIdApplication (Client) ID for appregistreringen
-CertificateThumbprintVelger innloggingssertifikatet fra det lokale sertifikatlageret via thumbprint; kombinasjonen av -ClientId og sertifikat gir en app-only-innlogging uten bruker
-NoWelcomeUndertrykker velkomstteksten etter innlogging; nyttig for skriptutdata og logger

6. Les meldinger og last ned ZIP-vedlegg

Skriptet kan nå gå gjennom innboksen, lagre og pakke ut ZIP-vedlegg samt flytte behandlede meldinger til «Slettede elementer». Nedlastingen skjer via endepunktet /$value og Invoke-MgGraphRequest -OutputFilePath. Dermed havner råinnholdet direkte i en fil uten å holde et stort vedlegg fullt ut i minnet:

Add-Type -AssemblyName System.IO.Compression.FileSystem
$Zielordner = "D:\Import\{0:yyyyMMdd_HHmmss}" -f (Get-Date)

$messages = Get-MgUserMessage -UserId $Mailbox -Top 100 `
    -Property id, subject, hasAttachments

foreach ($msg in $messages) {
    $ordner = Join-Path $Zielordner $msg.Id
    New-Item -Path $ordner -ItemType Directory -Force | Out-Null

    $anhaenge = Get-MgUserMessageAttachment -UserId $Mailbox -MessageId $msg.Id |
        Where-Object {
            $_.AdditionalProperties['@odata.type'] -eq '#microsoft.graph.fileAttachment' -and
            $_.Name -like '*.zip'
        }

    foreach ($att in $anhaenge) {
        $zip = Join-Path $ordner $att.Name
        $uri = "https://graph.microsoft.com/v1.0/users/$Mailbox/messages/$($msg.Id)/attachments/$($att.Id)/`$value"
        Invoke-MgGraphRequest -Method GET -Uri $uri -OutputFilePath $zip
        [System.IO.Compression.ZipFile]::ExtractToDirectory($zip, $ordner)
    }

    # flytt behandlet e-post til «Slettede elementer»
    Move-MgUserMessage -UserId $Mailbox -MessageId $msg.Id -DestinationId "deleteditems" | Out-Null
}
Forklaring av alternativer
AlternativEffekt
Add-Type -AssemblyName System.IO.Compression.FileSystemLaster .NET-assemblyen med klassen ZipFile for utpakking
Get-MgUserMessage -UserIdPostboks der meldingene skal leses; ved app-only-innlogging er angivelsen obligatorisk
-Top 100Begrenser spørringen til maksimalt 100 meldinger per kall
-Property id, subject, hasAttachmentsBer bare om feltene som trengs; dette reduserer svaret og gjør kallet raskere
Get-MgUserMessageAttachment -MessageIdMelding der vedleggene skal listes opp
Invoke-MgGraphRequest -Method GETHTTP-metode for direktekallet mot Graph API-et
-UriEndepunktet som kalles; den tilføyde /$value leverer vedleggets rå filinnhold i stedet for et JSON-objekt
-OutputFilePathSkriver svaret direkte til målfilen uten å holde hele vedlegget i minnet
Move-MgUserMessage -DestinationId "deleteditems"Flytter den behandlede meldingen til målmappen; deleteditems er det velkjente mappenavnet for «Slettede elementer»

Ved mer enn 100 e-poster, bruk Get-MgUserMessage -All eller paging; for en månedlig kjøring er én batch som regel nok.

7. Send rapport-e-post via Graph

Også Send-MailMessage er utdatert. Via den samme appregistreringen (rettighet Mail.Send) sendes e-posten direkte via Graph, her med en fil som base64-kodet vedlegg:

$pfad = "D:\Reports\report.csv"
$body = @{
    message = @{
        subject = "eCall Report"
        body    = @{ contentType = "HTML"; content = "<b>Lauf erfolgreich</b>" }
        toRecipients = @(@{ emailAddress = @{ address = "empfaenger@example.com" } })
        attachments  = @(@{
            "@odata.type" = "#microsoft.graph.fileAttachment"
            name          = Split-Path $pfad -Leaf
            contentBytes  = [Convert]::ToBase64String([IO.File]::ReadAllBytes($pfad))
        })
    }
    saveToSentItems = $true
}
Send-MgUserMail -UserId "reporting@example.com" -BodyParameter $body
Forklaring av alternativer
AlternativEffekt
-UserIdPostboks som e-posten sendes på vegne av; må dekkes av virkeområdet til Application Access Policy
-BodyParameterHele meldingen som hashtable i Graph-skjemaet: message med emne, brødtekst, mottakere og vedlegg samt saveToSentItems for lagring i «Sendte elementer»

8. Kjør uten tilsyn

Som en planlagt oppgave kjører skriptet uten innlogging, fordi sertifikatet ligger i kontoens store:

$action  = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument '-NoProfile -ExecutionPolicy Bypass -File "D:\Scripts\graph-import.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At 06:00
Register-ScheduledTask -TaskName "eCall-Graph-Import" -Action $action -Trigger $trigger `
    -User "DOMAIN\svc-ecall" -Password (Read-Host "Passwort")
Forklaring av alternativer
AlternativEffekt
New-ScheduledTaskAction -ExecuteProgrammet som skal kjøres, her powershell.exe
-ArgumentKommandolinje for programmet: -NoProfile hopper over profilskript, -ExecutionPolicy Bypass omgår skriptpolicyen for dette kallet, -File angir skriptet
New-ScheduledTaskTrigger -Daily -At 06:00Daglig utløser klokken 06:00
Register-ScheduledTask -TaskNameNavn på oppgaven i oppgaveplanleggeren
-Action / -TriggerKnytter den tidligere opprettede handlingen og utløseren til oppgaven
-UserKontoen oppgaven kjører under; den private nøkkelen må være lesbar i sertifikatlageret dens
-Password (Read-Host "Passwort")Ber interaktivt om kontopassordet slik at oppgaven kan starte uten en innlogget bruker; dermed havner det ikke i skriptet eller historikkfilen

Det komplette eksempelet med logging og feilbehandling finnes på GitHub: pfstr/eCall-Log-Analyzer.

Kilder

  1. Microsoft – «Retirement of Exchange Web Services in Exchange Online»

    kunngjøring og datoen (1. oktober 2026) for avslutningen av EWS i Exchange Online.

    https://techcommunity.microsoft.com/blog/exchange/retirement-of-exchange-web-services-in-exchange-online/3924440
  2. Microsoft Learn – «Get access without a user (App-only)»

    app-only-autentisering mot Microsoft Graph med sertifikat.

    https://learn.microsoft.com/en-us/graph/auth-v2-service
  3. Microsoft Learn – «Limiting application permissions to specific mailboxes»

    Application Access Policy for å begrense appen til enkelte postbokser.

    https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access

Kommentarer

Kommentarene lastes inn fra GitHub / Giscus.