PowerShell ostaje glavni alat za upravljanje Exchange Onlineom, iako klijenti i partneri Microsofta 365 sve više traže RESTful rješenje. Iako u ovom trenutku Microsoft ne nudi takvo rješenje za upravljanje Exchange Onlineom, oni su riješili neke od nedostataka PowerShella tijekom proteklih nekoliko godina. Čak su otišli i korak dalje, pružajući skup “nativnih” RESTful cmdleta, a kasnije i “proxy” “klasičnih” PowerShell cmdleta u JSON učitavanju preko standardnog HTTPS zahtjeva. Zauzvrat, sve to znači da se više ne morate čak ni oslanjati na PowerShell i da možete izravno pozvati odgovarajuće krajnje točke i metode.
Za svakoga tko prati evoluciju upravljanja Exchange Online, ništa od navedenog ne bi trebala biti vijest. Zapravo, pokrili smo ta poboljšanja u nekoliko članaka na ovom blogu, poput onog koji detaljno opisuje kako izravno pozvati metode koje pokreću nove cmdlete temeljene na REST-uili ovaj s detaljima InvokeCommand proxy metoda. Ali kako pitanja o navedenim metodama neprestano pljušte, a još uvijek ne postoji službena dokumentacija na koju bi se ljudi mogli uputiti, vrijeme je za jedan, opsežan članak (ili dva) koji se bave svime što trebate znati. Pa da vidimo o tome.
Prije nego počnemo
Kao prvo, ovdje je obavezan podsjetnik da Microsoft službeno ne podržava nijednu od ovih metoda, stoga ih koristite na vlastitu odgovornost. Promjene se mogu dogoditi u bilo kojem trenutku i bit će nedokumentirane, što znači da ćete možda morati potrošiti puno dodatnog vremena na održavanje svog koda. Microsoft bi jednostavno mogao odlučiti isključiti korištenje takvih metoda, na primjer implementacijom ograničenja na aplikaciju za pozivanje i slično. Nemojte reći da niste bili upozoreni!
Važno je razumjeti da trenutno postoje dva različita skupa cmdleta, iako oba koriste istu krajnju točku, https://outlook.office365.com/adminapi/beta/. Bilo koji od “klasičnih” Exchange Online cmdleta s kojima ste navikli raditi sada se proksira putem /InvokeCommand metoda, dok skup dodatnih metoda pokreće nove cmdlete temeljene na REST-u. Osim toga, dostupno je nekoliko drugih skrivenih metoda, iako se nijedna od njih ne preslikava izravno na postojeći cmdlet (iako pružaju funkcionalnost sličnu onoj postojećih cmdlet).
Još jedna stvar koju treba napomenuti je da ovo nije dio Graph API-ja, i iako se mogu povući određene paralele, postoji vrlo malo razlika kojih morate biti svjesni. Također možemo tvrditi da ni ovo nije “standardni” RESTful API. Drugim riječima, iako možete očekivati malo familijarnosti, nemojte se iznenaditi kada stvari ne rade kako se očekuje, ili uopće. Na primjer, neke od metoda mogu tretirati vrijednosti parametara kao osjetljive na velika i mala slova.
Rukovanje autentifikacijom
Kako biste izvršili bilo kakvu operaciju protiv Exchange Online REST API-ja, morate rukovati provjerom autentičnosti. Postoji nekoliko načina na koje možete to učiniti. Prvo, možete koristiti ugrađenu logiku u modulu Exchange Online PowerShell, koji podržava dopuštenja za delegate i aplikacije, kao što je opisano u službenoj dokumentaciji. Alternativno, možete se povezati prosljeđivanjem pristupnog tokena dobivenog za Exchange Online resurs bilo kojom od podržanih MSAL metoda. Možete koristiti vlastitu aplikaciju ili ugrađenu PowerShell temeljen na Microsoft Exchange REST API-ju jedan, s appID-om od fb78d390-0c51-40cd-8e17-fdbfab77341b. Budući da čitate ovaj članak, vjerojatno niste zainteresirani za izravno ili neizravno korištenje PowerShell-a, tako da vjerojatno nećete koristiti nijednu od gore navedenih metoda.
Umjesto toga, pretpostavka je da želite iskoristiti vlastitu Entra ID aplikaciju. Da biste to omogućili, morat ćete dodati odgovarajuća dopuštenja u smislu opsega resursa (uloga) i dopuštenja dodijeljenih glavnom objektu usluge. Budući da su ti koraci sada marljivo dokumentirani u službeni članak podrškekao i kod nas prethodni člancipreskočit ćemo većinu detalja. Trebate verziju TL;DR Razmjena.Upravljanje ili Exchange.ManageAsApp opseg, ovisno o modelu koji koristite, u kombinaciji s ulogom Exchange Online. Obavezno slijedite cijeli niz uputa jer je uobičajeni problem zanemarivanje jednog ili više koraka, što će kasnije rezultirati raznim pogreškama.
Imajući gore navedeno na umu, evo kako dobiti pristupni token u kontekstu određenog korisnika (koristeći model dopuštenja delegata). Za ovaj scenarij mnogo je lakše dobiti pristupni token korištenjem ugrađenih MSAL metoda. To zahtijeva da odgovarajuće binarne datoteke budu prisutne na lokalnom uređaju, što danas ne bi trebao biti problem, budući da ih svaki PowerShell modul povezan s Microsoft 365 koristi. Alternativno, možete instalirati odgovarajući NuGet paket.
Imajte na umu da se binarne datoteke i dostupne metode razlikuju od platforme do platforme, stoga svakako zamijenite kod u nastavku nečim što funkcionira na vašoj strani. Možete iskoristiti bilo koji noviji modul Microsoft 365 ili Entra, samo nemojte miješati različite module (ili čak verzije), jer može doći do sukoba. Ovdje koristimo ExO modul, jer u vrijeme pisanja Graph SDK za PowerShell (2.32.0) koristi malo zastarjelu verziju na MSAL binarnim datotekama (4.67.2).
#Load the MSAL binaries
Get-Module ExchangeOnlineManagement -ListAvailable | select -First 1 | select -ExpandProperty FileList | %
if ($_ -match "Microsoft.Identity.Client.dll
Primjer ispod pokazuje kako možete dobiti pristupni token putem MSAL-a AcquireTokenInteractive metoda. Što se tiče stvarne provjere autentičnosti, potreban nam je ID javne klijentske aplikacije, koji može biti ugrađena aplikacija za više korisnika koju Microsoft koristi (s appId vrijednost od fb78d390-0c51-40cd-8e17-fdbfab77341b) ili prilagođeni. Za potonji scenarij provjerite je li Razmjena.Upravljanje dat je pristanak na opseg. The resurs tražit ćemo token za to Office 365 Exchange Onlines ID-om od 00000002-0000-0ff1-ce00-000000000000. Možemo koristiti zadana vrijednost opseg, u ovom slučaju predstavlja https://outlook.office365.com/.default.
#Request a token via the built-in "Microsoft Exchange REST API Based Powershell" app
$app = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create("fb78d390-0c51-40cd-8e17-fdbfab77341b").WithDefaultRedirectUri().Build()
$Scopes = New-Object System.Collections.Generic.List[string]
$Scope = "https://outlook.office365.com/.default"
$Scopes.Add($Scope)
$token = $app.AcquireTokenInteractive($Scopes).WithLoginHint("user@domain.com").ExecuteAsync().Result
#Request a token for a custom app
$app = [Microsoft.Identity.Client.PublicClientApplicationBuilder]::Create("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").WithRedirectUri("http://localhost").Build()
$Scopes = New-Object System.Collections.Generic.List[string]
$Scope = "https://outlook.office365.com/.default"
$Scopes.Add($Scope)
$token = $app.AcquireTokenInteractive($Scopes).ExecuteAsync().Result
Postoji mnoštvo dodatnih scenarija o kojima ovdje možemo govoriti, ali to nije svrha ovog članka. Jednostavno provjerite jeste li nabavili važeći token za resurs Office 365 Exchange Onlinemetoda/pojedinosti nisu važni. Čak i stare ADAL metode još uvijek rade, ako ih iz nekog razloga želite koristiti. Upravljani identiteti također su podržani, ali ne zaboravite da podržavaju samo dopuštenja aplikacija.
Kad smo već kod toga, evo kako dobiti pristupni token u kontekstu aplikacije (poznatiji kao model dopuštenja aplikacije ili tijek vjerodajnica klijenta). Ova se metoda obično preferira za sve neinteraktivne scenarije, ali ne podržava je svaki cmdlet, kao što je detaljno opisano u službena dokumentacija.
#Obtain a token via client secret (application permissions)
$app = [Microsoft.Identity.Client.ConfidentialClientApplicationBuilder]::Create("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx").WithClientSecret("secret_goes_here").WithTenantId("tenant.onmicrosoft.com").Build()
$Scopes = New-Object System.Collections.Generic.List[string]
$Scope = "https://outlook.office365.com/.default"
$Scopes.Add($Scope)
$token = $app.AcquireTokenForClient($Scopes).ExecuteAsync().Result
Za razliku od scenarija dopuštenja delegata koji smo opisali gore, ovdje je potrebna uloga Exchange.ManageAsApp jedan, koji naravno možete pronaći pod dopuštenjima aplikacije. Ne zaboravite također dodijeliti odgovarajuću Exchange Online (ili Enrta ID) ulogu principalu usluge, jer će inače biti dostupan vrlo ograničen skup cmdleta. Ovo je zapravo jedna od glavnih razlika u odnosu na Graph API, gdje dopuštenja aplikacije obično daju neograničen pristup cijelom stanaru.
Samo za zabavu, evo i kako izvući token iz bilo koje trenutno aktivne Exchange Online PowerShell sesije:
$context = [Microsoft.Exchange.Management.ExoPowershellSnapin.ConnectionContextFactory]::GetAllConnectionContexts() $context.PowerShellTokenInfo.AuthorizationHeader $context.GetAuthHeader()
Korištenje metode InvokeCommand
Sada kada imamo važeći pristupni token, možemo izvršiti cmdlete korištenjem InvokeCommand metoda. Za ovo bismo željeli JSON sadržaj s pojedinostima o cmdlet-u i svim željenim parametrima. Osim toga, za uspješno izvođenje potrebno je nekoliko dodatnih zaglavlja, što je još jedna razlika u ponašanju u usporedbi s Graph API-jem. Da budemo precizniji, zahtjev mora sadržavati barem X-AnchorMailbox zaglavlje, što će pomoći u usmjeravanju na ispravan poslužitelj na pozadini. Za scenarije delegiranja dopuštenja, postavite vrijednost na UPN dotičnog korisnika. Za dopuštenja aplikacije umjesto toga koristi se UPN poštanskog sandučića sustava, što je korisno jer ga možete čvrsto kodirati. Ne zaboravite zamijeniti vrijednost ID-a stanara. Također možete koristiti zadanu domenu stanara, tenant.onmicrosoft.com
SystemMailboxbb558c35-97f1-4cb9-8ff7-d53741dc928c@xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Nekoliko dodatnih zaglavlja može se koristiti za promjenu vrste izlaza (X-ResponseFormats vrijednostima bilo kojeg json ili clixml) ili najveća veličina stranice (radije s vrijednošću od odata.maxpagesize=intgdje int može se kretati do 1000). Za naše potrebe ne trebamo ulaziti u ovu razinu detalja jer nijedno od ovih dodatnih zaglavlja nije obavezno. Dakle, možemo koristiti nešto poput ovoga za naše zahtjeve:
#Set the headers for request using delegate permissions
$authHeader = @
'Content-Type'='application/json'
'Authorization'="Bearer $($token.AccessToken)"
'X-ResponseFormat'= "json"
'X-AnchorMailbox' = "UPN:user@domain.com"
#Set the headers for request using application permissions
$authHeader = @
'Content-Type'='application/json'
'Authorization'="Bearer $($token.AccessToken)"
'X-ResponseFormat'= "json"
'X-AnchorMailbox' = "UPN:SystemMailboxbb558c35-97f1-4cb9-8ff7-d53741dc928c@tenant.onmicrosoft.com"
Što se tiče korisnog opterećenja, stvari mogu varirati od vrlo jednostavnih do prilično složenih, ovisno o cmdletima koje planirate koristiti. Za neke scenarije možda ćete čak morati pribjeći hvatanju sirovog izvršavanja cmdleta putem Fiddlera kako biste provjerili ispravnu sintaksu. Kao što je gore spomenuto, na kraju dana ovo ostaje nedokumentirana i nepodržana metoda, ali ako cmdlet radi kako se očekuje putem ExO modula, moći ćete ga pokrenuti putem InvokeCommand kao!
Ispod su neki primjeri JSON nosivosti (dobro, tehnički, PowerShell hash tablica koju kasnije pretvaramo u JSON). Prvi primjer će izvršiti Get-Poštanski sandučić cmdlet protiv specifičnih Identitet vrijednost, drugim riječima provjerit će postoji li određeni poštanski sandučić. Drugi primjer je malo kompliciraniji. Dodaje adresu e-pošte postojećem skupu Adrese e-pošte za poštanski sandučić putem Set-Poštanski sandučić cmdlet. Ne samo da zahtijeva dva parametra za pokretanje, već za jedan od njih moramo specificirati tip podataka.
#Payload for the Get-Mailbox cmdlet
$body = @
CmdletInput = @
CmdletName="Get-Mailbox"
Parameters=@Identity="vasil"
| ConvertTo-Json -Depth 5
#Payload for the Set-Mailbox cmdlet with a hash-table input parameter
$body = @
CmdletInput = @
CmdletName="Set-Mailbox"
Parameters=@Identity="MailboxName";EmailAddresses=@add="user@domain.com";"@odata.type"="#Exchange.GenericHashTable"
| ConvertTo-Json -Depth 5
Napokon smo spremni izdati naš zahtjev. Jedna stvar koju smo ranije zaboravili spomenuti je da je krajnja točka koju ćemo koristiti specifična za zakupca, morate navesti ID zakupca (ili zadanu domenu) kao dio nje kako biste osigurali ispravno izvršenje. Osim toga, ovo je samo standardni POST zahtjev s JSON sadržajem i konfiguriranim zaglavljima.
$uri = "https://outlook.office365.com/adminapi/beta/tenant.onmicrosoft.com/InvokeCommand" $res = Invoke-WebRequest -Method POST -Uri $uri -Headers $authHeader -Body $body ($res.Content | ConvertFrom-Json).Value
Izlaz, ako ga ima, bit će pohranjen unutar $res varijabla u traženom formatu (json u gornjem primjeru). Pregledavajući ga, mogli biste primijetiti da su neka svojstva vraćena zajedno s pratećim @data.type i/ili @odata.type braće i sestara za pomoć pri serijalizaciji (tj. da vam kažu vrstu podataka pohranjenih unutar vrijednosti svojstva). Na primjer:
ExchangeGuid@data.type : System.Guid ExchangeGuid@odata.type : #Guid ExchangeGuid : e61c9754-f72d-4b79-a45e-0d3279ee5b3f
Kao savjet, možete koristiti $odaberi operator kako bi poslužitelj vraćao samo određena svojstva:
$uri = 'https://outlook.office365.com/adminapi/beta/tenant.onmicrosoft.com/InvokeCommand?$select=Name,PrimarySmtpAddress,EmailAddresses' $res = Invoke-WebRequest -Method POST -Uri $uri -Headers $authHeader -Body $body ($res.Content | ConvertFrom-Json).Value[0] EmailAddresses@odata.type EmailAddresses PrimarySmtpAddress Name ------------------------- -------------- ------------------ ---- #Collection(String) smtp:2022test@tenant.onmicrosoft.com, SMTP:G45acdcf8ecf7490d9dff6737af48ea4f@domain.com G45acdcf8ecf7490d9dff6737af48ea4f@domain.com 2022test
Rješavanje pogrešaka može biti malo zeznuto jer ćete možda morati analizirati cijeli objekt. Evo primjera pogreške koju dobijete kada traženi objekt nije pronađen:
"error":
"code": "NotFound",
"message": "Error executing cmdlet",
"details": [
The operation couldn\u0027t be performed because object \u0027sharedddddd\u0027 couldn\u0027t be found on \u0027VE1PR03A01DC001.EURPR03A001.prod.outlook.com\u0027."
],
"innererror":
"message": "Error executing cmdlet",
"type": "Microsoft.Exchange.Admin.OData.Core.ODataServiceException",
"stacktrace": "",
"internalexception":
"message": "Exception of type \u0027Microsoft.Exchange.Management.PSDirectInvoke.DirectInvokeCmdletExecutionException\u0027 was thrown.",
"type": "Microsoft.Exchange.Management.PSDirectInvoke.DirectInvokeCmdletExecutionException",
"stacktrace": ""
,
"adminapi.warnings@odata.type": "#Collection(String)",
"@adminapi.warnings": []
Ima još puno primjera koje možemo istražiti, ali članak je i ovako dovoljno dugačak. Dobra vijest je da se uvijek možete osloniti na ugrađene cmdlete modula Exchange Online PowerShell u slučaju da naiđete na probleme s InvokeCommand metoda. Konfigurirati Fiddler za snimanje PowerShell prometapokrenite cmdlet, ispitajte snimanje, profit!
U dio 2 ovog članka, ispitat ćemo metode koje koriste REST cmdleti, kao i mnoštvo “skrivenih” metoda kojima možete pristupiti putem istih krajnjih točaka. Budući da se centar za sigurnost i sukladnost PowerShell također temelji na pozadini ExO, pokrit ćemo i cmdlete koje on podržava. I još zanimljivije stvari!