Wykonanie skryptu w Exchange Online

W przypadku Exchange Onpremise wykonywanie zautomatyzowanych skryptów było proste, wystarczyło dodać na serwerze zadanie do harmonogramu i tyle. W przypadku Exchange Online sprawa jest bardziej skomplikowana gdyż należy przed wykonaniem skryptu należy podłączyć się do Exchange Online a to wymaga uwierzytelnienia. Ze względów bezpieczeństwa Microsoft wycofał uwierzytelnianie podstawowe i w celu wykonania takiego skryptu należy skorzystać z nowoczesnej autoryzacji (modern authentication) jednocześnie nie chcemy aby dla takiego konta serwisowego wyłączone było uwierzytelnianie wieloskładnikowe. Wraz z wprowadzeniem Exchange Online V2 Powershell module version 2.0.3-preview Microsoft dodał możliwość logowania z wykorzystaniem certyfikatów.
Do wykonywania skryptów nie powinniśmy korzystać z konta posiadające uprawnienia global admina. Możemy jednak skorzystać z aplikacji, której nadamy tylko wybrane uprawnienia.

Na początek należy utworzyć aplikację z uprawnieniami API. Uruchamiamy okno Powershell-a z uprawnieniami administratora, tworzymy katalog Exo_App na dysku C, przechodzimy do niego i logujemy się do Azure AD

Connect-AzureAD

Po zalogowaniu należy wykonać poniższy skrypt, który w Azure utworzy aplikację o nazwie Exo_V2_App i nada jej uprawnienia

#Kod rejestrujący aplikację, nadanie uprawnień API
#Definicja nazwy aplikacji
$appName = 'Exo_V2_App’

##Pobranie parametrów API Office 365 Exchange Online
$api = (Get-AzureADServicePrincipal -Filter „AppID eq '00000002-0000-0ff1-ce00-000000000000′”)

##Pobranie ID uprawnień API
$permission = $api.AppRoles | Where-Object { $_.Value -eq 'Exchange.ManageAsApp’ }

##Utworzenie objektu zabezpieczeń API (TYPE: Role = Application, Scope = User)

$apiPermission = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
ResourceAppId = $api.AppId ;
ResourceAccess = [Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = $permission.Id ;
Type = "Role"
}}

Rejestracja nowej aplikacji w Azure wraz z uprawnieniami API
$myApp = New-AzureADApplication -DisplayName $appName -ReplyUrls 'http://localhost' -RequiredResourceAccess $apiPermission

##Enable the Service Principal
$mySP = New-AzureADServicePrincipal -AppID $myApp.AppID

##Pokaż właściwości nowej aplikacji
$apiPermission = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{
ResourceAppId = $api.AppId ;
ResourceAccess = [Microsoft.Open.AzureAD.Model.ResourceAccess]@{
Id = $permission.Id ;
Type = "Role"
}}

##Rejestracja nowej aplikacji w Azure wraz z uprawnieniami API
$myApp = New-AzureADApplication -DisplayName $appName -ReplyUrls 'http://localhost' -RequiredResourceAccess $apiPermission

##Enable the Service Principal
$mySP = New-AzureADServicePrincipal -AppID $myApp.AppID

##Pokaż właściwości nowej aplikacji
$myApp | Format-List DisplayName,ObjectID,AppID

Ostatni wiersz prezentuje następujące parametry aplikacji DisplayName,ObjectID,AppID które zachowane zostają w zmiennej $myApp. Wartości te możemy zapisać do pliku csv

$myApp | Export-Csv -NoTypeInformation "$($appName).csv"

Następnym krokiem jest przypisanie odpowiednich uprawnień do aplikacji. Przypisujemy tylko rolę Exchange Administrator

## The role to assign to your app
$directoryRole = 'Exchange Administrator'

## Find the ObjectID of 'Exchange Administrator'
$RoleId = (Get-AzureADDirectoryRole | Where-Object {$_.displayname -eq $directoryRole}).ObjectID

## Add the service principal to the directory role
Add-AzureADDirectoryRoleMember -ObjectId $RoleId -RefObjectId $mySP.ObjectID -Verbose

Kolejną czynnością jest wygenerowanie samodzielnie podpisanego certyfikatu (self-signed) i podłączenie go do naszej aplikacji. Możemy skorzystać ze skryptu Create-SelfSignedCertificate. Powyższy skrypt wygeneruje samodzielnie podpisany certyfikat używając nazwę naszej aplikacji. W przypadku gdy chcemy zmienić czas wazności certyfikatu nalezy odpowiednio zmodyfikowac zmienną $certYears. W zmiennej $certPassword podajemy hasło do certyfikatu

## Ważność certyfikatu w latach
$certYears = 3

## Hasło certyfikatu (PFX)
$certPassword = 'YXg7{67E@(I7[:h'

.\Create-SelfSignedCertificate.ps1 -CommonName $appName `
-StartDate (Get-Date).AddDays(-1) `
-EndDate (Get-Date).AddYears($certYears) `
-Password (ConvertTo-SecureString $certPassword -AsPlainText -Force) `
-Force

W wyniku wykonania powyższego skryptu zostaną utworzone 2 pliki Exo_V2_App.pfx oraz Exo_V2_App.cer.
Następnie należy zaimportować wygenerowany certyfikat do naszej aplikacji w Azure. Poniższy kod zlokalizuje certyfikat (plik .cer) w naszym folderze roboczym i załaduje go do aplikacji w Azure AD.

## Pobierz plik certyfikatu (.CER)
$CertificateFilePath = (Resolve-Path ".\$($appName).cer").Path
## Utwórz objekt certyfikatu
$cer = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cer.Import("$($CertificateFilePath)")
$bin = $cer.GetRawCertData()
$base64Value = [System.Convert]::ToBase64String($bin)
$bin = $cer.GetCertHash()
$base64Thumbprint = [System.Convert]::ToBase64String($bin)

## Załaduj certyfikat i przypisz go do aplikacji w AzureAD
$null = New-AzureADApplicationKeyCredential -ObjectId $myApp.ObjectID `
-CustomKeyIdentifier $base64Thumbprint `
-Type AsymmetricX509Cert -Usage Verify `
-Value $base64Value `
-StartDate ($cer.NotBefore) `
-EndDate ($cer.NotAfter)

Po wykonaniu powyższego kodu nie powinniśmy zobaczyc żadnej informacji wynikowej chyba, że wystąpi błąd.
W Azure AD -> Azure Active Directory -> App Registration możemy sprawdzić czy certyfikat został załadowany

W kolejnym kroku administrator globalny musi wyrazić zgodę na działanie aplikacji. W tym celu w przeglądarce otwieramy następujący link

https://login.microsoftonline.com/{TenantID}/adminconsent?client_id={ApplicationID}

W miejscu {TenantID} podajemy ID naszego tenanta, natomiast zamiast {ApplicationID} podajemy ID naszej aplikacji. Tenant ID możemy uzyskać wydając z powershell polecenie

$tenantID = (Get-AzureADTenantDetail).ObjectID

lub logując się na stronie Portalu Azure -> Azure Active Directory -> Overview

natomiast ID aplikacji Portalu Azure -> Azure Active Directory -> App Registrations -> All application ->wybór naszej aplikacji

Poniższy kod w powershell wygeneruje odpowiedni link i go uruchomi

## Pobierz TenantID
$tenantID = (Get-AzureADTenantDetail).ObjectID

## Browse this URL
$consentURL = "https://login.microsoftonline.com/$tenantID/adminconsent?client_id=$($myApp.AppId)"

## Wyświetl utworzony link
$consentURL

## Otwórz link w domyślnej przeglądarce
Start-Process $consentURL

Po wykonaniu wszystkich powyższych czynności możemy połączyć się Exchange Online poprzez naszą aplikację z wykorzystaniem certyfikatu.

Uwierzytelnienie z wykorzystaniem certyfikatu w postaci plik pfx. Do tego celu wymagane jest TenantID, AppID, certyfikat (ścieżka do certyfikatu) oraz hasło do certyfikatu. Poniższy kod pozwoli na połączenie się do Exchange Online (oczywiście należy dokonać odpowiednich zmian do własnego środowiska)

## ustawienie tenant ID (directory ID or domain)
$tenantID = 'firma.onmicrosoft.com'

## Ustawienie ID aplikacji Exo_V2_App
$appID = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'

## Ustawienie ścieżki do certyfikatu (.pfx)
$CertificateFilePath = 'C:\exo_v2_demo\Exo_V2_App.pfx'

## Hasło do certyfikatu
$pfxPassword = 'yyyyyyyyyyyy'

## Podłączenie do Exchange Online
Connect-ExchangeOnline -CertificateFilePath $CertificateFilePath `
-CertificatePassword (ConvertTo-SecureString -String $pfxPassword -AsPlainText -Force) `
-AppID $appID `
-Organization $tenantID

Poniżej powyższego kodu możemy dodać własny skrypt.