Querying Azure ACS endpoint using PowerShell

Querying Azure ACS endpoint using PowerShell

This blog post presents the implementation in order to query the Microsoft Azure Active Directory Access Control (also known as Access Control Service or ACS) using an OData client written in PowerShell.  The first step consists in retrieving a Simple Web Token (SWT) issued by ACS from a management credential. Then, the retrieved access token allows to access the ACS Management Service entities.

Context

As a Dynamics CRM developer, I was willing to learn how to write a listener for a Microsoft Azure solution, that can read and process Microsoft Dynamics CRM Online messages posted to the Microsoft Azure Service Bus. In order to post Dynamics CRM Online messages and the associated execution context, I followed the walkthrough explaining how to register an Azure-aware plug-in with the CRM plug-in registration tool.Once the endpoint registration succeeded, I visited the ACS Management Portal by curiosity to see the trust relationships rules created. The following four rules were created, including one defining CRM Online as the claim issuer.

ASCManagementPortal

However, when I clicked on this first rule, an unexpected error occurred:

UnexpectedError

Please, do tell me more about this error:

ErrorDetail

The detail points to the error code ACS80001. As documented in the ACS Error Code page, this error is the only error specific to the ACS Management Portal.

  • HTTP Error Code: 404
  • Message: This rule is configured to use a Claim Issuer type that is not supported by the management portal. Please use the management service to view and edit this rule.
  • Action required to fix the error: This error occurs if a rule is configured to use an Issuer that is not an identity provider or the Access Control Service “LOCAL AUTHORITY” issuer. For details on how to use the ACS Management Service, see ACS Management Service.

The root cause is that the rule is configured to use an Issuer that is not an identity provider or the Access Control Service “LOCAL AUTHORITY” issuer. This is true as the Issuer is crm4.dynamics.com.

Since there is no error to fix as the configuration seems to be working, I decided to query the management service as suggested in order to view the rule and maybe get more details about the rule created.

Requirements

Software requirements

Since we will query ACS using an OData client, there is no particular requirements regarding the set-up of any libraries or SDK assemblies related with Azure. The exhaustive requirements are listed in ACS Prerequisites:

  • .NET Framework 4
  • Windows Identity Foundation SDK
  • Windows Identity Foundation Runtime

Since this post is about PowerShell, I would add: your favourite PowerShell IDE.

N.B.: The implementation presented in the following lines is based on the documentation about How to: Use ACS Management Service to Configure Rules and Rule Groups. The instructions are modified and converted into PowerShell syntax.

Collect required ACS configuration information

We will need to collect a few information regarding ACS authentication and the target Azure hostname and namespace.

  • User: in the ACS Management Portal, click Management service under the Administration section.  The default management account is displayed in the Management Service Accounts table. For example, mine was SBManagementClient.
  • Password: next, retrieve the value of the ACS Management Service account password by editing the previous account. Then, click on Password in the Credentials table. You can display the password value by clicking on the button Show password.
  • Namespace: then, you can find the name of your Azure namespace from the URL of your ACS Management Portal. For example, http://hiyoko2-sb.accesscontrol.windows.net, the namespace is hiyoko2-sb.
  • Hostname: finally, identify the ACS hostname from the URL of your ACS Management Portal. Usually, it is accesscontrol.windows.net.

You can now initialize your PowerShell script by declaring the following variables:

# ACS configuration information
$user = "SBManagementClient"
$password = "+PgvciFZq6xKqnTdsCgATB9Du8UKbQS14vQ4y0ve+IE=" # This is indeed not my real password
$namespace = "hiyoko-sb"
$hostname = "accesscontrol.windows.net"
$relativeUrl = "v2/mgmt/service/"

Request a Token from ACS via the OAuth WRAP Protocol

The next step is to request a token from ACS via the OAuth WRAP Protocol. We will request our token by sending an SWT token as documented:

This method requires the client to send a SWT token which can be signed with a service identity symmetric key or an identity provider symmetric key to ACS via the OAuth WRAP protocol for authentication.

A possible implementation is as follow:

Function GetTokenFromAcs
{
    $baseAddress = "https://$namespace.$hostname"
    $endpoint = "https://$namespace.$hostname/$relativeUrl"

    $client = New-Object -TypeName System.Net.Webclient
    $client.BaseAddress = $baseAddress

    $values = New-Object -TypeName Collections.Specialized.NameValueCollection
    $values.Add("grant_type", "client_credentials")
    $values.Add("client_id", $user)
    $values.Add("client_secret", $password)
    $values.Add("scope", $endpoint)

    $responseBytes = $client.UploadValues("/v2/OAuth2-13", "POST", $values) 
    $response = [System.Text.Encoding]::UTF8.GetString($responseBytes)

    $serializer = New-Object -TypeName System.Web.Script.Serialization.JavaScriptSerializer
    $decodedDictionary = $serializer.DeserializeObject($response)

    if ($decodedDictionary -eq $null)
    {
        throw "An error occured while deserializing the access token: the response is null."
    }
    return $decodedDictionary["access_token"]
}

Note that:

  • In this implementation, we use a System.Net.WebClient in order to perform the request, calling the UploadValues method.
  • Then, we use a System.Web.Script.Serialization.JavaScriptSerializer object instance in order to deserialize the returned JavaScript object.

You can now retrieve an access token by calling the function:

$token = GetTokenFromAcs

Querying Azure ACS endpoint

We have now an access token which allows us to call the Azure ACS endpoint.

Function GetResource
{
    Param (
        [string]
        $Resource
    )

    $requestUrl = "https://$namespace.$hostname/$relativeUrl" + $Resource
    $headers = @{
        "Accept-Charset" = "UTF-8";
        "Authorization" = "Bearer " + $token;
        "Host" = "$namespace.$hostname"
    }
    $response = Invoke-WebRequest -Uri $requestUrl -Headers $headers -Method Get
    return $response.Content
}

Note that:

  • The access token is placed in an authorization directive inside the HTTP request header.
  • The request is sent using the Invoke-WebRequest cmdlet.

Now you can call the ACS endpoint in order to retrieve all information about the rule defined with crm4.dynamics.com as an Issuer:

$rule = GetResource -Resource "Rules()?`$filter=Issuer/Name eq 'crm4.dynamics.com'"
Write-Host "Rule: $rule"

The response looks like the following:

<feed xml:base="https://hiyoko-sb.accesscontrol.windows.net/v2/mgmt/service/" xmlns:d="http://schemas.microsoft.com/ado/2007/08/dataservices" xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Rules</title>
  <id>https://hiyoko-sb.accesscontrol.windows.net/v2/mgmt/service/Rules</id>
  <updated>2015-03-24T12:20:08Z</updated>
  <link rel="self" title="Rules" href="Rules" />
  <entry m:etag="W/&quot;X'0000000000C06A79'&quot;">
    <id>https://hiyoko-sb.accesscontrol.windows.net/v2/mgmt/service/Rules(26935507L)</id>
    <title type="text"></title>
    <updated>2015-03-24T12:20:08Z</updated>
    <author>
      <name></name>
    </author>
    <link rel="edit" title="Rule" href="Rules(26935507L)" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/Issuer" type="application/atom+xml;type=entry"	title="Issuer" href="Rules(26935507L)/Issuer" />
    <link rel="http://schemas.microsoft.com/ado/2007/08/dataservices/related/RuleGroup" type="application/atom+xml;type=entry" title="RuleGroup" href="Rules(26935507L)/RuleGroup" />
    <category term="Microsoft.Cloud.AccessControl.Management.Rule" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"></category>
    <content type="application/xml">
      <m:properties>
        <d:id m:type="Edm.Int64">26935507</d:id>
        <d:rulegroupid m:type="Edm.Int64">21005122</d:rulegroupid>
        <d:issuerid m:type="Edm.Int64">21299051</d:issuerid>
        <d:description>sampleorgsendrule</d:description>
        <d:inputclaimtype>http://schemas.microsoft.com/xrm/2011/Claims/Organization</d:inputclaimtype>
        <d:inputclaimvalue>hiyoko2.crm4.dynamics.com</d:inputclaimvalue>
        <d:outputclaimtype>net.windows.servicebus.action</d:outputclaimtype>
        <d:outputclaimvalue>Send</d:outputclaimvalue>
        <d:systemreserved m:type="Edm.Boolean">false</d:systemreserved>
        <d:version m:type="Edm.Binary">AAAAAADAank=</d:version>
      </m:properties>
    </content>
  </entry>
</feed>

Now, you can simply parse the result as an XML file.

The value of InputClaimType node is as expected of the walkthrough dedicated on how to create the equivalent rule manually, without the help of the Plugin Registration Tool: Configure Microsoft Azure ACS for integration with Microsoft Dynamics CRM 2015. A difference is that the configuration of the rule manually uses LOCAL AUTHORITY as an Issuer ; and does not use the service identity crm4.dynamics.com as an Issuer.

Handle exceptions

Do not forget to handle exceptions by wrapping your code using Try Catch block. Various authentication or network issues may occur, so you definitively want to understand what went wrong when a request failed.

Try {
    # Your code here
}
Catch
{
    Write-Error -Message $($_.Exception | Format-List -Force | out-string)
    
    $innerexception = $_.Exception.InnerException
    While ($innerexception)
    {
        Write-Error -Message "Inner Exception:"
        Write-Error -Message $($innerexception | Format-List -Force | out-string)

        $innerexception = $innerexception.InnerException
    }
}

What’s next?

Using the ACS Management Service API Reference, you can now query entities metadata, records and associations between them, using PowerShell. A next step would be to implement the edition of records, and automate required ACS rules deployment.

Leave a Reply

Your email address will not be published.