Azure Functions for Muhimbi Power Users and IT pro’s

In this post we will consume the Muhimbi SOAP service in an Azure function that can further be invoked as a REST API in Microsoft Flow, PowerApps, and even from your own PowerShell Console. 

Why would you want to add this complexity to your life, especially as a perfectly good Online REST based API is available at little to no cost?  Well, in our posts we aim to show you the flexibility of The PDF Converter and the numerous, perhaps not obvious, ways it can be used.  As the IT sphere becomes more complex, with a mix of on-premise and online solutions all working together, a basic cookie-cutter solution just doesn’t cut it anymore.  Solutions need to be flexible and able to adapt to the unique environment that exists around each deployment.

One of the reasons you may want to take this approach is compliancy. In some industries it is just not allowed to use 3rd party web services. The approach in this blog post allows you to control the entire process, software and hardware, end-to-end.

Note: This post covers fairly advanced topics, so you’ll need to have at least a basic knowledge of Azure and PowerShell in order to reproduce the methods described here.


Before we begin, please make sure the following prerequisites are in place:

  1. Install and configure a Windows Server or VM with a public facing IP address.
  2. Download the fully functional free trial of The Muhimbi PDF Converter Services here.
  3. Please make sure The Muhimbi PDF Converter Services  is installed exactly as described in chapter 2 of the Administration Guide.  Please follow that guide to the letter and make sure you pay particular good attention to the section about ‘dependencies’.  The Administration Guide is included in the download and available on-line here.
  4. You’ll also need an Azure subscription and appropriate privileges to create Azure Functions.


Now that we have the prerequisites in place, the first question may well be “What is an Azure Function”?

Azure Functions are “serverless” compute services that let you run event-triggered code without having to explicitly provision or manage infrastructure.

In the above statement, the term “serverless” might sound a bit confusing.  The term “serverless” just means that you do not need to worry about the actual hardware, patching of the server, updates, etc.  The only thing you need to concern yourself with is the code you run.  In this post we will use PowerShell code and create an Azure Function.


Why are we using PowerShell in an Azure Function?  Well

PowerShell is the language of choice for system administrators and power-users.  With the introduction of PowerShell in Azure functions, we can host and run our PowerShell code “serverless”, just like all the cool kids do!

Note: If you’re an advanced developer, then you can use the programming language of your choice with Azure Functions.  You can find an overview of the various code samples in the Muhimbi Github repository.


Now to get started with the actual steps, we need to accomplish:

  • STEP 1 A-C: Create an Azure Function.
  • STEP 2 A-D: Write a PowerShell Script to invoke a SOAP API to convert documents to PDF.
  • STEP 3: Invoke / Test the Function from a PowerShell Prompt.
  • STEP 4: Optional final step to invoke the Function using Microsoft Flow.

Step 1A:  Navigate to “Function apps” in the Azure portal and click “Add”.  That will bring up the “Create” menu. Here, you have the option to select “PowerShell” as the Runtime stack.

Your Function Apps screen should look like the one below:


Step 1B: Now that you have your Azure “Function App” created, let’s create an Azure Function. To Create a new Function, click on the “Functions +” symbol and click on the  “HTTP trigger”.

For more details about Azure Function Triggers and Bindings see this article.

  • Enter the Function “Name” and “Authorization level” and click on “Create”.

Step 1C: As you have just created your PowerShell Trigger, you will see two files in the “View Files” blade.

  • run.ps1 – This is a default PowerShell file that runs when we hit the Azure Function Endpoint (URL).
  • function.json – This is the file that contains the configuration metadata for the function.

Note: The “function.json” file can be used do do many things, but the PowerShell script above should suffice.


Step 2A: As this method is an ‘in Portal’ Azure Function, you can create your PowerShell script using the Editor of your choice (Visual Studio code, PowerShell ISE etc.). 

A sample PowerShell script to convert a document to PDF using our SOAP API can be found below. 

Note: Change the url to “host name / IP Address” to the server running the Muhimbi Service in the PowerShell Script below:

Function ConvertTo-PDF
{
<#
    .SYNOPSIS
        Converting Office files to PDF Format using the Muhimbi Web Services based interface

    .DESCRIPTION
        Convert popular document types to PDF or XPS format with near perfect fidelity. Support is available for MS-Word, PowerPoint, Excel, InfoPath, Visio and MS-Publisher etc.

    .EXAMPLE
        Execute-Convert -FileName clavin.txt -SourceFileContent TXVoaW1iaSByb2Nrcw==
    #>
    [CmdletBinding()]
    Param( 
        [String]
        $FileName,

        [String]
        $SourceFileContent
    ) 
    #** The URL where the Web Service is located. Amend your server host name
    $url= 'http://<your_server_url>:41734/Muhimbi.DocumentConverter.WebService/?wsdl'

    #** The SOAPAction HTTP header for the Convert Action
    $soapAction = 'http://services.muhimbi.com/2009/10/06/DocumentConverterService/Convert'

    #** The Source File Content
    $sourceFileContent = $SourceFileContent

    #** Get the Extension of the Source File
    $fileExtension = [System.IO.Path]::GetExtension($FileName)
    $extension = $fileExtension.Replace('.','')

    #** OutPut File Format
    $format = 'PDF'

    #** The SOAP Request Body for the Convert Action
    $soapBody ='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://services.muhimbi.com/2009/10/06" xmlns:ns1="http://types.muhimbi.com/2009/10/06" xmlns:ns2="http://types.muhimbi.com/2014/02/06" xmlns:ns3="http://types.muhimbi.com/2015/04/13" xmlns:ns4="http://types.muhimbi.com/2010/05/17" xmlns:ns5="http://types.muhimbi.com/2013/08/01" xmlns:ns6="http://types.muhimbi.com/2013/01/14">'+
   '<soapenv:Header/>'+
   '<soapenv:Body>'+
      '<ns:Convert>'+
         '<ns:sourceFile>'+$sourceFileContent+'</ns:sourceFile>'+
         '<ns:openOptions>'+
            '<ns1:FileExtension>'+$fileExtension+'</ns1:FileExtension>'+
            '<ns1:OriginalFileName>'+$FileName+'</ns1:OriginalFileName>'+
         '</ns:openOptions>'+
         '<ns:conversionSettings>'+
            '<ns1:Format>PDF</ns1:Format>'+
            '<ns1:Fidelity>Full</ns1:Fidelity>'+
            '<ns1:ConverterSpecificSettings/>'+
            '<ns1:OutputFormatSpecificSettings/>'+
         '</ns:conversionSettings>'+
      '</ns:Convert>'+
   '</soapenv:Body>'+
'</soapenv:Envelope>'

    try
    {
        #** Sending SOAP Request To Server 
        $soapWebRequest = [System.Net.WebRequest]::Create($url)
        $soapWebRequest.Headers.Add("SOAPAction", $soapAction)
        $soapWebRequest.ContentType = "text/xml"
        $soapWebRequest.Method = "POST"

        #** Initiating Send
        $writer = New-Object System.IO.StreamWriter $soapWebRequest.GetRequestStream()
        $writer.WriteLine($soapBody)
        $writer.Close()

        #** Send Complete, Waiting For Response.
        $response = $soapWebRequest.GetResponse() 
        $responseStream = $response.GetResponseStream() 
        $soapReader = [System.IO.StreamReader]($responseStream) 
        $returnXml = [Xml] $soapReader.ReadToEnd() 
        $responseStream.Close() 
        return $returnXml
    }
    catch
    {
        Write-Error $Error[0]
    }
}
    $soapResponseXML = ConvertTo-PDF -FileName clavin.txt -SourceFileContent TXVoaW1iaSByb2Nrcw==
    [xml]$xml = $soapResponseXML.InnerXML
    $processedFileContent = $xml.Envelope.Body.ConvertResponse.InnerText
    Write-Host $processedFileContent


Step 2B: Copy and paste the above script in your PowerShell ISE, then save it and run it. You should get the output below:


Step 2C: In this Step we will use the above script in our Azure Function.

Most of the Script is self-explanatory but let me try to break it down a bit with the help of the screenshot below:

Now that you have a gist of how the code works, just copy paste the PowerShell Script below into your Function:

using namespace System.Net

#** Input bindings are passed in via param block.
param($Request, $TriggerMetadata)

#** Write to the Azure Functions log stream.
Write-Host "PowerShell HTTP trigger function processed a request."

#** ConvertTo-PDF Function

Function ConvertTo-PDF
{
<#
    .SYNOPSIS
        Converting Office files to PDF Format using the Muhimbi Web Services based interface

    .DESCRIPTION
        Convert popular document types to PDF or XPS format with near perfect fidelity. Support is available for MS-Word, PowerPoint, Excel, InfoPath, Visio and MS-Publisher etc.

    .EXAMPLE
        Execute-Convert -FileName clavin.txt -SourceFileContent TXVoaW1iaSByb2Nrcw==
    #>
    [CmdletBinding()]
    Param( 
        [String]
        $FileName,

        [String]
        $SourceFileContent
    ) 
    #** The URL where the Web Service is located. Amend your server host name
    $url= 'http://<your_server_url>:41734/Muhimbi.DocumentConverter.WebService/?wsdl'

    #** The SOAPAction HTTP header for the Convert Action
    $soapAction = 'http://services.muhimbi.com/2009/10/06/DocumentConverterService/Convert'

    #** The Source File Content
    $sourceFileContent = $SourceFileContent

    #** Get the Extension of the Source File
    $fileExtension = [System.IO.Path]::GetExtension($FileName)
    $extension = $fileExtension.Replace('.','')

    #** OutPut File Format
    $format = 'PDF'

    #** The SOAP Request Body for the Convert Action
    $soapBody ='<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns="http://services.muhimbi.com/2009/10/06" xmlns:ns1="http://types.muhimbi.com/2009/10/06" xmlns:ns2="http://types.muhimbi.com/2014/02/06" xmlns:ns3="http://types.muhimbi.com/2015/04/13" xmlns:ns4="http://types.muhimbi.com/2010/05/17" xmlns:ns5="http://types.muhimbi.com/2013/08/01" xmlns:ns6="http://types.muhimbi.com/2013/01/14">'+
   '<soapenv:Header/>'+
   '<soapenv:Body>'+
      '<ns:Convert>'+
         '<ns:sourceFile>'+$sourceFileContent+'</ns:sourceFile>'+
         '<ns:openOptions>'+
            '<ns1:FileExtension>'+$fileExtension+'</ns1:FileExtension>'+
            '<ns1:OriginalFileName>'+$FileName+'</ns1:OriginalFileName>'+
         '</ns:openOptions>'+
         '<ns:conversionSettings>'+
            '<ns1:Format>PDF</ns1:Format>'+
            '<ns1:Fidelity>Full</ns1:Fidelity>'+
            '<ns1:ConverterSpecificSettings/>'+
            '<ns1:OutputFormatSpecificSettings/>'+
         '</ns:conversionSettings>'+
      '</ns:Convert>'+
   '</soapenv:Body>'+
'</soapenv:Envelope>'

    try
    {
        #** Sending SOAP Request To Server 
        $soapWebRequest = [System.Net.WebRequest]::Create($url)
        $soapWebRequest.Headers.Add("SOAPAction", $soapAction)
        $soapWebRequest.ContentType = "text/xml"
        $soapWebRequest.Method = "POST"

        #** Initiating Send
        $writer = New-Object System.IO.StreamWriter $soapWebRequest.GetRequestStream()
        $writer.WriteLine($soapBody)
        $writer.Close()

        #** Send Complete, Waiting For Response.
        $response = $soapWebRequest.GetResponse() 
        $responseStream = $response.GetResponseStream() 
        $soapReader = [System.IO.StreamReader]($responseStream) 
        $returnXml = [Xml] $soapReader.ReadToEnd() 
        $responseStream.Close() 
        return $returnXml
    }
    catch
    {
        Write-Error $Error[0]
    }
}

#** Interact with query parameters or the body of the request.
$filename = $Request.Query.filename
$SourceFileContent = $Request.Query.SourceFileContent

if (-not $SourceFileContent) {
    $filename = $Request.Body.filename
    $SourceFileContent = $Request.Body.SourceFileContent
	$soapResponseXML = ConvertTo-PDF -FileName clavin.txt -SourceFileContent TXVoaW1iaSByb2Nrcw==
    [xml]$xml = $soapResponseXML.InnerXML
    $processedFileContent = $xml.Envelope.Body.ConvertResponse.InnerText
}

if ($filename) {
    $status = [HttpStatusCode]::OK
    $body = "$processedFileContent"
}
else {
    $status = [HttpStatusCode]::BadRequest
    $body = "Please pass a name on the query string or in the request body."
}

#** Associate values to output bindings by calling 'Push-OutputBinding'.
Push-OutputBinding -Name Response -Value ([HttpResponseContext]@{
    StatusCode = $status
    Body = $body
})


Step 2D: Run the Function with reference to the screenshot below:


Step 3: Now that we have our Azure Function in place. let’s test it using PowerShell. However, first we need to “Get the Function URL”. You can get the URL as per the screenshot below:

Open the PowerShell Prompt and paste the PowerShell Script below and run it.

Note: In the PowerShell script, both the “Filename” and “SourceFileContent” are mandatory parameters.

$Body = @(
    @{
        SourceFileContent='TXVoaW1iaSByb2Nrcw=='
        Filename='clavin.txt'
    }
)
$params = @{
    Uri         = 'https://<Your_Server_Name>.azurewebsites.net/api/HttpTrigger1?code=xxxxxxxxxxxx=='
    Method      = 'POST'
    Body        =  $Body| ConvertTo-Json
    ContentType = 'application/json'
}

Invoke-RestMethod @params

Finally and optionally, Step 4:

Want to Invoke the Function using Microsoft Flow? If yes, click here for a detailed explanation.


Subscribe to this blog for the latest updates about SharePoint Online, Microsoft Flow, Power Apps and document conversion and manipulation.

2 thoughts on “Azure Functions for Muhimbi Power Users and IT pro’s

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