CUCM AXL API with Powershell – update Partitions

Hi guys,

With automation and bulk tasks getting more and more important in big UC environments, and well known methods like BAT jobs getting obsolete, I wanted to do a quick introduction to CUCM AXL API and show, based on an example, how easy and powerful it is.

Let’s take a look at the script below. It’s written in PowerShell and its goal is to update Partition of multiple Directory Numbers at once, in much, much quicker way than via BAT or GUI.

Script with descriptions goes like this:

# PowerShell

# Variable with your CUCM Server IP/FQDN
$cucmServer = "1.1.1.1"
# Variable with CUCM/API version - '12.0' for CUCM 12.5, '11.5' for CUCM 11.5, "10.5" for CUCM 10.5
$version = '12.0'

# This function will ask you for credentials when you run the script. Remember that user you want to use to connect to CUCM needs to be assigned with AXL role.
if ( ! ($cred) ) {
$cred = Get-Credential
}

# Now we're getting into the part where we specify input data for the AXL request
# NOTE - for the script to work you need to have at least 2 Directory Numbers provided in the "DN" array below

# Array with directory numbers
$DN = "1000", "1001"
# Defining old partition
$oldPartition= "PT1"
# Defining new partition
$newPartition = "PT2"

# This is the loop which iterates over "DN" array defined couple lines above. For each DN we will create brand new AXL request
for($i = 0; $i -lt $DN.length; $i++)
{

$x = $DN[$i]

# Here we prepare the the content or body of our AXL request. This is the code being processed by CUCM AXL interface when received
$request = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/12.0">
<soapenv:Header/>
<soapenv:Body>
<ns:updateLine>
<pattern>$x</pattern>
<routePartitionName>$oldPartition</routePartitionName>
<newRoutePartitionName>$newPartition</newRoutePartitionName>
</ns:updateLine>
</soapenv:Body>
</soapenv:Envelope>
"@

# This part is to 'style' the output file of our script. For each and every request sent to CUCM, we will receive either positive or negative response. We just want to have it in file so we know which Directory Number properly updated its Partition, and which did not.
"`r`n #### REQUEST NUMBER $i ####" | Out-File -FilePath .\output.txt -Append
$request | Out-File -FilePath .\output.txt -Append
"#### RESPONSE ####" | Out-File -FilePath .\output.txt -Append

# Below is to prepare a full SOAP request which we send to CUCM. This contains headers required by CUCM to accept a request, as well as a AXL body which contains the actual information of what we do want to change. The body was prepared couple steps above.
# The 'try' and 'catch' syntax always go together in PowerShell. Idea is that in 'try' you specify action you want to try to perform, which in this case is to form and send AXL request. The 'catch' part is for error handling.
try {
$result = Invoke-RestMethod -Method Post -Uri "https://$cucmServer`:8443/axl/" -Headers @{'Content-Type'='text/xml';'SOAPAction'='CUCM:DB ver=' + $version} -Body $request -Credential $cred
}
catch {
$_ | select -ExpandProperty ErrorDetails | Select -ExpandProperty Message | Out-File -FilePath .\output.txt -Append
}
$result.envelope.body.updateLineResponse.return | Out-File -FilePath .\output.txt -Append
}

Let’s focus on explaining most important part, which is why AXL request itself.

AXL is a SOAP API. SOAP has its defined structure – Envelope, Header, Body. Out of these, Envelope and Body are mandatory, meaning you need to put something inside them.

In our request you can see part which says:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/12.0">

Why the envelope looks like that? Because it was defined this way by Cisco. Every time you send AXL request, the envelope part needs to look this way for CUCM to accept and process your request (you can find all this in Cisco/AXL/SOAP documentation).

Then we have Header, which is empty because it can be (it’s not mandatory), and then we have main part – the Body.

<soapenv:Body>
<ns:updateLine>
<pattern>$x</pattern>
<routePartitionName>$oldPartition</routePartitionName>
<newRoutePartitionName>$newPartition</newRoutePartitionName>
</ns:updateLine>
</soapenv:Body>

In Body we specify an actual AXL method we want to use, in this case it’s “updateLine” method. How did I now what values and headers to use? For this we have to take a look at the official Cisco DevNet AXL schema for our request:

https://pubhub.devnetcloud.com/media/axl-schema-reference/docs/Files/AXLSoap_UpdateLineReq.html#LinkF69

Let me place here a snippet from above link:

There are three important parts to understand when reading AXL schema:

  • boxes with solid line frame are mandatory
  • boxes with dashed line frame are optional
  • the “option” symbol tells us that we have make a choice between available options

So above schema tells us, that to use “UpdateLineReq” method, we either have to use the “uuid” (PKID, database identifier of a Directory Number), or, we have to use the “pattern” which basically is a number itself. If we don’t provide one of these, CUCM will not understand which line we want to process (quite logical isn’t it?).

All other headers you can see in the schema are optional, you can use them depending on the DN parameters you would like to adjust.

So – in my case, I wanted to update Partition, and I decided to define my Directory Number by “pattern” parameter. That’s exactly why I put <pattern>, <routePartitionName>, <newRoutePartitionName>, in the request Body.

It works similarly with all other methods described in “https://developer.cisco.com/docs/axl-schema-reference/“. It takes some time to get familiar with it, but it’s really not that difficult.

When we run our script, output file will look somehow like this:

 #### REQUEST NUMBER 0 ####
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/12.0">
    <soapenv:Header/>
    <soapenv:Body>
        <ns:updateLine>
            <pattern>1000</pattern>
            <routePartitionName>PT1</routePartitionName>
            <newRoutePartitionName>PT2</newRoutePartitionName>
        </ns:updateLine>
    </soapenv:Body>
</soapenv:Envelope>
#### RESPONSE ####
soapenv:ServerDirectory Number not found5003Directory Number not foundupdateLine

 #### REQUEST NUMBER 1 ####
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://www.cisco.com/AXL/API/12.0">
    <soapenv:Header/>
    <soapenv:Body>
        <ns:updateLine>
            <pattern>1001</pattern>
            <routePartitionName>PT1</routePartitionName>
            <newRoutePartitionName>PT2</newRoutePartitionName>
        </ns:updateLine>
    </soapenv:Body>
</soapenv:Envelope>
#### RESPONSE ####
{DBB5F715-A055-79A2-3AA2-729F1A6A8A3B}

Now, a bit of explanation on the output.

As you can see, each request is marked with a text description which we defined in the script. Right after there is an actual AXL content sent to CUCM, so you can see which Directory Number we aim for and what Partition we want to change on it.

Difference comes after we print out our response. In the first case, we can see a text string saying “ServerDirectory Number not found5003Directory Number not foundupdateLine”, while in the second case we got some mysterious string of “{DBB5F715-A055-79A2-3AA2-729F1A6A8A3B}”.

Long story short – the first request failed, while the second succeeded.

The message you see for the failed request is a standard CUCM response for this type of request, whenever it can not be processed by the system. The reason why it couldn’t be processed is quite straightforward – in CUCM there was no Directory Number “1000” with Partition “PT1”. If it wasn’t there, CUCM couldn’t change it – as simple as that.

For the properly processed request you can see “{DBB5F715-A055-79A2-3AA2-729F1A6A8A3B}”. This is a unique identifier of Directory Number 1001 stored in CUCM Database (PKID). The reason why we see it as a response is because… that’s how body of AXL response for that request has been defined by developers. Instead of returning “OK” or “Success”, or whatever, they decided to return PKID of a Directory Number. Once we see it, we know operation has been accomplished and Partition got changed.

And that’s it! Once you put data into script (DNs and Partitions names), you just need to run it from PowerShell console, and in a matter of seconds you can update tens of Directory Numbers. For those of you who know the pain of such operational/migration tasks, just imagine how long it would take you do to the same via BAT or GUI.

Thanks!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s