Tuesday, August 15, 2017

Using Dynamics365 Customer Engagement admin API with PowerShell, part1

Last week Microsoft released a new API for administering MSDYN365 Customer Engagement instances.
The API is REST based and easy to use, but as it is completely new it only supports OAuth authentication (which means no more simple cookie auth).
There is a sample included which does everything you need to get it working, but I made a few modifications to the AuthenticationHelper class to reuse it both for the MSDYN365 Customer Engagement API as well as the admin api.

So what I'm going going to do for this series is show you how to register an Azure Application and host this yourself (or you can just check out my github repo for the Dynamics365-PoSh)

Also, check out part2 here and part3 here

Create a new class project and scaffolding a commandlet

The first thing we'll do is to create a new class project for .Net Framework in Visual Studio (download link). Next, add the following NuGet packages:

Next up we start by scaffolding our initial class. Rename it to GetDynamicsInstances.cs, then inherit from the Cmdlet class. Decorate your class with cmdlet specifications, and create an overridden ProcessRecord method

[Cmdlet(VerbsCommon.Get, "DynamicsInstances")]
public class GetDynamicsInstances : PSCmdlet
    [Parameter(Mandatory = true)]
    [ValidateSet("NorthAmerica", "SouthAmerica", "Canada", "EMEA", "APAC", "Oceania", "Japan", "India", "NorthAmerica2", "UnitedKingdom", IgnoreCase = true)]
    public string Location;

    protected override void ProcessRecord()

With this we have a method that takes in a verifiable location. We can now compile, open up a PowerShell session, and import the compiled DLL as a module using the following line

Import-Module .\MSDYN365AdminApiAndMore.dll

When we try to run our Get-DynamicsInstances commandlet we can tab through the predefined set of locations.

Adding URL generator and base method for authentication

I've created a folder named Helpers, and added a class named UrlFactory. I've made the class static, and added a public enum to the end of the file to prevent usage of magic variables.

public enum DataCenterLocations
    NorthAmerica = 1,
    SouthAmerica = 2,
    Canada = 3,
    EMEA = 4,
    APAC = 5,
    Oceania = 6,
    Japan = 7,
    India = 8,
    NorthAmerica2 = 9,
    UnitedKingdom = 11

Next up is adding a method for generating a URL to use for the admin API. We'll take a location enum and an operation string as input. In addition, we'll add a static string for the URL format which we'll use to return the complete URI.

public static string BaseUrl = "https://{0}.crm{1}.dynamics.com{2}";
public static Uri GetUrl(string subdomain, DataCenterLocations location, string resource = "")
    if (location == DataCenterLocations.NorthAmerica)
        return new Uri(
            string.Format(BaseUrl, subdomain, "", resource)
        return new Uri(
            string.Format(BaseUrl, subdomain, (int)location, resource)

This allows us to call the GetUrl method with only a location and the resource we want to call. In case of north america there is no number appended to the crm subdomain, so we're filling in blank there.
Next we'll create a new helper name AuthenticationHelper. This will take care of the authentication for us, and it is based on the sample in the admin API docs.
For now, we'll just add a public constructor which takes in the server url, and sets a private string value to the authority (strips the path away from the Uri).

public class AuthenticationHelper
    private string _endpoint = null;
    public AuthenticationHelper(Uri endpoint)
        _endpoint = endpoint.GetLeftPart(UriPartial.Authority);

To utilize these new helpers we can parse the input in our Cmdlet to the corresponding enum, and then call the GetUrl method with the instances resource specified in the admin API
Then we'll use the Uri to instantiate a new Authentication class.

Enum.TryParse(Location, out DataCenterLocations tenantLocation);
var serverUrl = UrlFactory.GetUrl("admin.services", tenantLocation, "/api/v1/instances");


In this part we created a new class project for our PowerShell module and added some scaffolding to it. In the next part we will look into how the authentication works and flesh out the AuthenticationHelper class.

No comments:

Post a Comment