forked from microsoft/BCTech
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/microsoft/BCTech
- Loading branch information
Showing
11 changed files
with
456 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# Authenticating to the Business Central Admin Center API | ||
|
||
A main obstacle when calling APIs is how to authenticate. | ||
|
||
The Admin Center API is based on Azure Active Directory (AAD) authentication using the OAuth standard. OAuth can seem complicated at first, but once you understand the basics, it is easy to use and actually makes life much easier - especially when calling across AAD tenants. | ||
|
||
On this page, we explain a few simple steps that will get you up and running quickly. | ||
|
||
|
||
|
||
## Register a new Azure Active Directory (AAD) Application in your AAD Tenant | ||
|
||
1. Open https://portal.azure.com | ||
2. Register new application: | ||
- Click "App registrations" | ||
- Click "New registration" | ||
- Give a name such as "App for managing Business Central environments" | ||
- Select "Accounts in any organizational directory (Any Azure AD directory - Multitenant)" | ||
- Add a redirect URI: "Public client/native" and "nativeBusinessCentralClient://auth" | ||
- Click "Register" | ||
3. Create the AAD application's service principal: | ||
- On the AAD application's "Overview" page, locate the "Managed application in local directory" property | ||
- If the value is a link called "Create Service Principal", then click that link | ||
- Navigate back in the browser to get back to the AAD application's "Overview" page | ||
4. Make a note of the "Application (client) ID" | ||
5. Add permission to call Business Central APIs: | ||
- Click "API permissions" | ||
- Click "Add a permission" | ||
- Click "Microsoft APIs" | ||
- Click "Dynamics 365 Business Central" | ||
- Click "Delegated permissions" | ||
- Select "user_impersonation" | ||
- Click "Add permissions" | ||
|
||
|
||
**At this point**, the AAD application has been registered in your (the partner's) AAD tenant, and you should know these two values: | ||
|
||
$aadAppId = "<a guid>" | ||
$aadAppRedirectUri = "nativeBusinessCentralClient://auth" | ||
|
||
|
||
|
||
## Specifically for Delegated Admins | ||
|
||
This section only applies to partners who have a reseller relationship with customers, also known as Delegated Admins. | ||
|
||
As a delegated admin, you want to manage your customers' Business Central environments. From an authentication perspective, this is significantly different from managing your own Business Central environments. | ||
|
||
In order to call Business Central APIs for a customer, you need to authenticate **in the customer's AAD tenant**, not in your own AAD tenant. This process normally requires an administrator of the customer's AAD tenant to give consent to your AAD application, however, this step can be avoided for delegated admins by adding your AAD application into the Admin Agents group in your own AAD tenant. This technique is described in more detail here: https://docs.microsoft.com/graph/auth-cloudsolutionprovider. | ||
|
||
In short, you need to execute the following PowerShell commands (remember to set the $aadAppId variable): | ||
|
||
Install-Module AzureAD | ||
Connect-AzureAD | ||
$adminAgentsGroup = Get-AzureADGroup -Filter "displayName eq 'AdminAgents'" | ||
$servicePrincipal = Get-AzureADServicePrincipal -Filter "appId eq '$aadAppId'" | ||
Add-AzureADGroupMember -ObjectId $adminAgentsGroup.ObjectId -RefObjectId $servicePrincipal.ObjectId | ||
|
||
Now it's all set up, and you should be able to use your AAD application (in your AAD tenant) to call | ||
into your customers' Business Central environments (in their AAD tenants). | ||
|
||
|
||
|
||
## Obtain an Access Token | ||
|
||
You will use your newly registered AAD application to obtain a so-called *access token*, which is a long string that looks something like this: | ||
|
||
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkN0VHVoTUptRDVNN0RMZHpEMnYyeDNRS1NSWSIsImtpZCI6IkN0VHVoTUptRDVNN0RMZHpEMnYyeDNRS1NSWSJ9.eyJhdWQiOiJodHRwIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2...yMTc3MTQ1ZTEwIl19.LZgQnXOLNNpJgBx5q7FOUgq5ka04lJkBw75kxMTUA7hFDEL-NsMVcwQ_Zt-H0aPkOevCAQ_KWtZRQA | ||
|
||
This string encodes different information, including who you are, and which API you want to call. You don't need to know all about access tokens - the main thing to know is that in order to use it, you need to send it in the Authorization header of each HTTP request, like this: | ||
|
||
GET https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments | ||
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkN0VHVoTUptRDVNN0RMZHpEMnYyeDNRS1NSWSIsImtpZCI6IkN0VHVoTUptRDVNN0RMZHpEMnYyeDNRS1NSWSJ9.eyJhdWQiOiJodHRwIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2...yMTc3MTQ1ZTEwIl19.LZgQnXOLNNpJgBx5q7FOUgq5ka04lJkBw75kxMTUA7hFDEL-NsMVcwQ_Zt-H0aPkOevCAQ_KWtZRQA | ||
|
||
To obtain an access token in PowerShell, you can follow the steps in the [Authenticate.ps1](PowerShell/Authenticate.ps1) script. | ||
|
||
And to use the access token to call the Admin Center API, you can follow the steps in e.g. the [Environments.ps1](PowerShell/Environments.ps1) script. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# This file contains examples of API calls that can be used to manage apps for an environment. | ||
|
||
|
||
# Shared Parameters | ||
$environmentName = "MyProd" | ||
#$accessToken = "" # get this from the Authenticate sample | ||
|
||
|
||
# List installed apps | ||
Write-Host -ForegroundColor Cyan "Listing installed apps in environment $environmentName for a customer..." | ||
$response = Invoke-WebRequest ` | ||
-Method Get ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/apps" ` | ||
-Headers @{Authorization=("Bearer $accessToken")} | ||
$installedApps = ConvertTo-Json (ConvertFrom-Json $response.Content) # prettify json | ||
Write-Host $installedApps | ||
|
||
|
||
# Get available updates | ||
Write-Host -ForegroundColor Cyan "Getting available app updates for environment $environmentName for a customer..." | ||
$response= Invoke-WebRequest ` | ||
-Method Get ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/apps/availableUpdates" ` | ||
-Headers @{Authorization=("Bearer $accessToken")} | ||
$availableUpdates = ConvertTo-Json (ConvertFrom-Json $response.Content) # prettify json | ||
Write-Host $availableUpdates | ||
|
||
|
||
# Update one of the apps | ||
$appIdToUpdate = "63ca2fa4-4f03-4f2b-a480-172fef340d3f" | ||
$appTargetVersion = "16.0.11240.12736" | ||
Write-Host -ForegroundColor Cyan "Scheduling an update of app $appIdToUpdate in environment $environmentName..." | ||
$response= Invoke-WebRequest ` | ||
-Method Post ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/apps/$appIdToUpdate/update" ` | ||
-Body (@{ | ||
targetVersion = $appTargetVersion | ||
} | ConvertTo-Json) ` | ||
-Headers @{Authorization=("Bearer $accessToken")} ` | ||
-ContentType "application/json" | ||
$operationStatus = ConvertTo-Json (ConvertFrom-Json $response.Content) # prettify json | ||
Write-Host $operationStatus | ||
|
||
|
||
# Check update status | ||
Write-Host -ForegroundColor Cyan "Checking status on the update operation..." | ||
$response= Invoke-WebRequest ` | ||
-Method Get ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/apps/$appIdToUpdate/operations" ` | ||
-Headers @{Authorization=("Bearer $accessToken")} | ||
$operations = ConvertTo-Json (ConvertFrom-Json $response.Content) # prettify json | ||
Write-Host $operations | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# This sample authenticates to Azure Active Directory (AAD) an obtains an access token. | ||
# The access token can be used for authenticating to Business Central APIs. | ||
|
||
|
||
# Parameters | ||
$identityClientPath = "C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.2.76\Microsoft.IdentityModel.Clients.ActiveDirectory.dll" # Install-Module AzureAD to get this | ||
$aadTenantId = "14ae87bb-40fe-434a-b185-8eea9645e857" # customer's tenant id | ||
$aadAppId = "a19cb26a-2e4c-408b-82e1-6311742ecc50" # partner's AAD app id | ||
$aadAppRedirectUri = "nativeBusinessCentralClient://auth" # partner's AAD app redirect URI | ||
|
||
|
||
# Load Microsoft.IdentityModel.Clients.ActiveDirectory.dll | ||
Add-Type -Path $identityClientPath | ||
|
||
|
||
# Get access token | ||
$ctx = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new("https://login.microsoftonline.com/$aadTenantId") | ||
$redirectUri = New-Object -TypeName System.Uri -ArgumentList $aadAppRedirectUri | ||
$platformParameters = New-Object -TypeName Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters -ArgumentList ([Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always) | ||
$accessToken = $ctx.AcquireTokenAsync("https://api.businesscentral.dynamics.com", $aadAppId, $redirectUri, $platformParameters).GetAwaiter().GetResult().AccessToken | ||
|
||
|
||
# Print access token | ||
Write-Host (ConvertTo-Json (ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($accessToken.Split('.')[1])))) -Depth 99) | ||
Write-Host -ForegroundColor Cyan 'Authentication complete - we have an access token for Business Central, and it is stored in the $accessToken variable.' | ||
|
||
|
||
# Extract the AAD tenant id from the access token. If you ever have an access token, and you want to know in which customer AAD tenant it is valid, you can execute this line: | ||
$aadTenantId = (ConvertTo-Json (ConvertFrom-Json ([System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($accessToken.Split('.')[1])))).tid) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
# TODO | ||
|
||
|
||
# How to get a storage account SAS URL: | ||
# TODO | ||
|
||
|
||
# Export environment to a .bacpac file in a storage account | ||
# TODO | ||
|
||
|
||
# Check export history | ||
# TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# This file contains examples of API calls that can be used to manage environments for a customer. | ||
# WARNING: the sample will modify some environments; execute this code at your own risk. | ||
|
||
|
||
# Shared Parameters | ||
#$accessToken = "" # get this from the Authenticate sample | ||
|
||
|
||
# Get list of environments | ||
Write-Host -ForegroundColor Cyan "Listing environments for a customer..." | ||
$response = Invoke-WebRequest ` | ||
-Method Get ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments" ` | ||
-Headers @{Authorization=("Bearer $accessToken")} | ||
$environments = ConvertTo-Json (ConvertFrom-Json $response.Content) # prettify json | ||
Write-Host $environments | ||
|
||
|
||
# Set AppInsights key | ||
$environmentName = "MyProd" | ||
$newAppInsightsKey = "00000000-1111-2222-3333-444444444444" | ||
$response = Invoke-WebRequest ` | ||
-Method Post ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/settings/appinsightskey" ` | ||
-Body (@{ | ||
key = $newAppInsightsKey | ||
} | ConvertTo-Json) ` | ||
-Headers @{Authorization=("Bearer $accessToken")} ` | ||
-ContentType "application/json" | ||
Write-Host "Responded with: $($response.StatusCode) $($response.StatusDescription)" | ||
|
||
|
||
# Get update window | ||
$environmentName = "MyProd" | ||
$response = Invoke-WebRequest ` | ||
-Method Get ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/settings/upgrade" ` | ||
-Headers @{Authorization=("Bearer $accessToken")} | ||
Write-Host "Update window: $(ConvertTo-Json (ConvertFrom-Json $response.Content) )" | ||
|
||
|
||
# set update window | ||
$environmentName = "MyProd" | ||
$preferredStartTimeUtc = "2000-01-01T02:00:00Z" | ||
$preferredEndTimeUtc = "2000-01-01T09:00:00Z" | ||
$response = Invoke-WebRequest ` | ||
-Method Put ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$environmentName/settings/upgrade" ` | ||
-Body (@{ | ||
preferredStartTimeUtc = $preferredStartTimeUtc | ||
preferredEndTimeUtc = $preferredEndTimeUtc | ||
} | ConvertTo-Json) ` | ||
-Headers @{Authorization=("Bearer $accessToken")} ` | ||
-ContentType "application/json" | ||
|
||
|
||
# Create new environment | ||
$newEnvironmentName = "MyNewSandbox" | ||
$response = Invoke-WebRequest ` | ||
-Method Put ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$newEnvironmentName" ` | ||
-Body (@{ | ||
EnvironmentType = "Sandbox" | ||
CountryCode = "DK" | ||
} | ConvertTo-Json) ` | ||
-Headers @{Authorization=("Bearer $accessToken")} ` | ||
-ContentType "application/json" | ||
|
||
|
||
# Copy production environment to a sandbox environment | ||
$environmentName = "MyProd" | ||
$newEnvironmentName = "MyNewSandboxAsACopy" | ||
$response = Invoke-WebRequest ` | ||
-Method Put ` | ||
-Uri "https://api.businesscentral.dynamics.com/admin/v2.1/applications/businesscentral/environments/$newEnvironmentName" ` | ||
-Body (@{ | ||
EnvironmentType = "Sandbox" | ||
CountryCode = "DK" | ||
copyFromEnvironmentName = $environmentName | ||
} | ConvertTo-Json) ` | ||
-Headers @{Authorization=("Bearer $accessToken")} ` | ||
-ContentType "application/json" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# This file contains examples of API calls that can be used to manage the notification recipients for a customer. | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This sample is relevant for partners who are registered as resellers for one or more customers. | ||
# It allows the partner to perform Partner Center operations programmatically, e.g., to enumerate customers, | ||
# see their subscriptions, etc. | ||
|
||
|
||
Install-Module PartnerCenter # only needs to be done once, see documentation at https://docs.microsoft.com/powershell/module/partnercenter | ||
|
||
|
||
# Authenticate to Partner Center | ||
Connect-PartnerCenter | ||
|
||
|
||
# Get all customers | ||
$customers = Get-PartnerCustomer | ||
Write-Host "Found $($customers.Length) customers." | ||
|
||
|
||
# Filter down the list for demo purposes | ||
$customers = $customers | ? -Property Name -Like "*MS Virtual Event*" | ||
Write-Host "Will work with these $($customers.Length) customers going forward:" | ||
$customers | Format-Table | ||
|
||
|
||
# Get subscriptions | ||
foreach ($customer in $customers) | ||
{ | ||
Write-Host "$($customer.CustomerId), $($customer.Domain), $($customer.Name) has the following subscriptions:" | ||
$subscriptions = Get-PartnerCustomerSubscription -InputObject $customer | ||
$subscriptions | Select-Object -Property Status,Quantity,FriendlyName,IsTrial,CommitmentEndDate,AutoRenewEnabled | Format-Table | ||
} |
Oops, something went wrong.