HornbillAPI.psm1

##############################
# Hornbill XMLMC API Azure Powershell Module
# v1.0.0
#
#.DESCRIPTION
# This module includes functions to allow your Powershell scripts to make and send API calls
# to your Hornbill instance, and process responses accordingly.
#
# Requires Powershell 3.0 or above.
#
#.NOTES
# See example scripts and function documentation for guidance on usage.
##############################

# Initialise the module-level variables
[string]$script:InstanceName = ""
[string]$script:InstanceZone = ""
[string]$script:APIKey = ""
[string]$script:XMLMCParams = ""
[string]$script:InstanceURL = ""

##############################
# .SYNOPSIS
# Allows you to define the Hornbill instance
#
#.DESCRIPTION
# MANDATORY - Allows your Powershell script to define the Hornbill instance to connect
# to, the zone in which it resides, and the API key to use for session generation.
#
#.PARAMETER Instance
# The (case-sensitive) instance name that you wish to connect to.
#
#.PARAMETER Zone
# The (case-sensitive) zone in which the Hornbill instance resides.
# If not supplied, defaults to: eur
#
#.PARAMETER Key
# The API key to use to generate authenticate against the Hornbill instance with.
#
#.EXAMPLE
# Set-HB-Instance "yourinstancename" "eur" "yourapikeygoeshere"
##############################
function Set-HB-Instance {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the name of the Instance to connect to (case sensitive)")]
        [ValidateNotNullOrEmpty()]
            [string]$Instance,
        
        [Parameter(Mandatory=$False, HelpMessage="Specify the Zone in which the Instance is run. Defaults to 'eur' (case sensitive)")]
            [string]$Zone="eur",

        [Parameter(Mandatory=$True, HelpMessage="Specify the API Key to authenticate the session against")]
        [ValidateNotNullOrEmpty()]
            [string]$Key
    )
    $script:InstanceName = $Instance
    $script:InstanceZone = $Zone
    $script:APIKey = $Key
    $script:InstanceURL = "https://"+$script:InstanceZone+"api.hornbill.com/"+$script:InstanceName+"/xmlmc/"
}

##############################
# .SYNOPSIS
# Define proxy details to connect through
#
#.DESCRIPTION
# If you are using a proxy to connect to the internet, then this function allows you to define
# the proxy address and authentication details (where applicable)
#
#.PARAMETER ProxyAddress
# Mandatory - URI of the Proxy server to connect through
#
#.PARAMETER ProxyCredentials
# Either:
# - String containing the username to authenticate against the proxy with (will prompt for password)
# - A PSCredential object, such as one generated by the Get-Credential cmdlet.
#
# By not providing this parameter, the module will use the credentials of the current Windows user
#
#.EXAMPLE
# Set-HB-Proxy "http://yourproxyaddress:80/"
# The above will route the requests through your proxy, and will authenticate using the current user details
#
# Set-HB-Proxy "http://yourproxyaddress:80/" "DOMAIN01\User01"
# The above will route the requests through your proxy, and will authenticate using "DOMAIN01\User01" account
##############################
function Set-HB-Proxy {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the address of your Proxy")]
        [ValidateNotNullOrEmpty()]
            [string]$ProxyAddress,
        [Parameter(Mandatory=$False, HelpMessage="Specify the credentials to authenticate against your Proxy")]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.CredentialAttribute()]
            $ProxyCredentials
    )
    $script:ProxyURI = $ProxyAddress
    if($ProxyCredentials) {
        $script:ProxyCreds = $ProxyCredentials
    }
}

##############################
# .SYNOPSIS
# Add a parameter to the XMLMC request
#
#.DESCRIPTION
# Add a parameter to the XMLMC request
#
#.PARAMETER ParamName
# Mandatory - the name of the parameter
#
#.PARAMETER ParamValue
# Mandatory - the [string] value of the parameter
#
#.PARAMETER ParamAllowEmpty
# Boolean, allow empty string to be passed as a parameter value
#
#.PARAMETER ParamAttribs
# Any attributes to add to the XMLMC request
#
#.EXAMPLE
# Add-HB-Param "application" "com.hornbill.servicemanager"
# Add-HB-Param "h_class" "computer" "onError=""omitElement"" "
#
# Note the escaped double-quotes in the ParamAttribs string.
##############################
function Add-HB-Param {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the name of the Parameter to add")]
        [ValidateNotNullOrEmpty()]
            [string]$ParamName,
        [Parameter(Mandatory=$False, HelpMessage="Specify the Value of the Parameter")]
            [string]$ParamValue = "",
        [Parameter(Mandatory=$False, HelpMessage="Specify attributes to add to the Parameter XML node")]
            [boolean]$ParamAllowEmpty = $False,
        [Parameter(Mandatory=$False, HelpMessage="Specify attributes to add to the Parameter XML node")]
            [string]$ParamAttribs = ""
    )
    if($ParamName.length -eq 0){
        return "Parameter name length needs to be greater than zero"
    }
    if(-not $ParamAllowEmpty -And $ParamValue -eq ""){
        return
    }

    $script:EncodedParamVal = ""
    if($ParamValue -ne ""){
        $script:EncodedParamVal = [System.Security.SecurityElement]::Escape($ParamValue)   
    }

    $CurrentParam = "<"+$ParamName
    if($ParamAttribs -and $ParamAttribs.length -gt 0){
        $CurrentParam = $CurrentParam + " " + $ParamAttribs
    }
    $CurrentParam = $CurrentParam + ">" + $EncodedParamVal + "</"+$ParamName+">"
    $script:XMLMCParams = $script:XMLMCParams + $CurrentParam
}

##############################
# .SYNOPSIS
# Open a new XML element
#
#.DESCRIPTION
# Allows for the building of complex XML
#
#.PARAMETER Element
# The name of the complex element to open
#
#.EXAMPLE
# Open-HB-Element "primaryEntityData"
##############################
function Open-HB-Element {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the name of the Parameter to add")]
        [ValidateNotNullOrEmpty()]
            [string]$Element
    )
    $script:XMLMCParams = $script:XMLMCParams + "<"+$Element+">"
}

##############################
# .SYNOPSIS
# Close an already open XML element
#
#.DESCRIPTION
# Allows for the building of complex XML
#
#.PARAMETER Element
# The name of the complex element to close
#
#.EXAMPLE
# Close-HB-Element "primaryEntityData"
##############################
function Close-HB-Element {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the name of the Parameter to add")]
        [ValidateNotNullOrEmpty()]
            [string]$Element
    )
    $script:XMLMCParams = $script:XMLMCParams + "</"+$Element+">"
}

##############################
# .SYNOPSIS
# Return XML parameters
#
#.DESCRIPTION
# Returns XML string of parameters that have been added by Add-HB-Params, Open-HB-Element or Close-HB-Element
#
#.EXAMPLE
# Get-HB-Params
##############################
function Get-HB-Params {
    if($script:XMLMCParams.length -gt 0) {
        return "<params>"+$script:XMLMCParams+"</params>"
    }
    return ""
}

##############################
# .SYNOPSIS
# Clear existing XML parameters
#
#.DESCRIPTION
# Clears any existing XMLMC parameters that have been added
#
#.EXAMPLE
# Clear-HB-Params
##############################
function Clear-HB-Params {
    $script:XMLMCParams = ""
}

##############################
# .SYNOPSIS
# Base64 encode a string
#
#.DESCRIPTION
# Returns a Base64 encoded string from a given UTF8 string
#
#.PARAMETER StringVal
# The string to encode
#
#.EXAMPLE
# ConvertTo-HB-B64Encode "encode this please"
##############################
function ConvertTo-HB-B64Encode {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the string to Base-64 encode")]
        [ValidateNotNullOrEmpty()]
            [string]$StringVal
    )
    $UnencodedBytes = [System.Text.Encoding]::UTF8.GetBytes($StringVal)
    $EncodedText =[Convert]::ToBase64String($UnencodedBytes)
    return $EncodedText
}

##############################
# .SYNOPSIS
# Decode a Base64 encoded string
#
#.DESCRIPTION
# Returns a UTF8 string from a given Base64 endcoded string
#
#.PARAMETER StringVal
# The string to decode
#
#.EXAMPLE
# ConvertTo-HB-B64Decode "ZW5jb2RlIHRoaXMgcGxlYXNl"
##############################
function ConvertTo-HB-B64Decode {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the Base-64 string to decode")]
        [ValidateNotNullOrEmpty()]
            [string]$B64Val
    )
    $DecodedString = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($B64Val))
    return $DecodedString
}

##############################
# .SYNOPSIS
# Invokes the XMLMC API call
#
#.DESCRIPTION
# Takes the API Service and Method as inputs to this function, and any parameters
# added with Add-HB-Param, Open-HB-Element or Close-HB-Element, and invokes an API call using
# the instance details defined with the Set-Instance function.
#
# Returns a Powershell Object containing:
# .status - the status of the API call or HTTP response
# .params - any returned parameters from the API
# .error - any returned errors if the HTTP request or API call fails
#
#.PARAMETER XMLMCService
# The Service that contains the API on the Hornbill instance
#
#.PARAMETER XMLMCMethod
# The API Method
#
#.EXAMPLE
# $xmlmcCall = Invoke-HB-XMLMC "session" "getSessionInfo"
#
# If successful This would return:
# $xmlmcCall.status = "ok"
# $xmlmcCall.params = A PSObject containing all output parameters returned by the
# session::getSessionInfo API
##############################
function Invoke-HB-XMLMC {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Specify the XMLMC Service")]
        [ValidateNotNullOrEmpty()]
            [string]$XMLMCService,
        [Parameter(Mandatory=$True, HelpMessage="Specify the XMLMC Method")]
        [ValidateNotNullOrEmpty()]
            [string]$XMLMCMethod
    )
    $script:responseStatus = ""
    $script:responseParams = ""
    $script:responseError = ""

    try {
        # Build XMLMC call
        $script:mcParamsXML = Get-HB-Params
        $script:bodyString = '<methodCall service="'+$XMLMCService+'" method="'+$XMLMCMethod+'">'+$script:mcParamsXML+'</methodCall>'
        $script:body = [XML]$script:bodyString

        # Build HTTP request headers
        $script:headers = @{}
        $script:headers["Content-Type"] ="text/xmlmc"
        $script:headers["Cache-control"]="no-cache"
        $script:headers["Authorization"]="ESP-APIKEY "+$script:APIKey
        $script:headers["Accept"]="text/xml"

        # Build URI for HTTP request
        $script:URI = $script:InstanceURL + $XMLMCService+"/?method="+$XMLMCMethod
       
        # Invoke HTTP request

        if($script:ProxyURI -and $script:ProxyURI -ne ""){
            if($script:ProxyCreds) {
                $script:ProxyCreds
                $r = Invoke-WebRequest -Uri $script:URI -UseBasicParsing -Method Post -Headers $script:headers -ContentType "text/xmlmc" -Body $script:body -ErrorAction:Stop -Proxy $script:ProxyURI -ProxyCredential $script:ProxyCreds
            } else {
                $r = Invoke-WebRequest -Uri $script:URI -UseBasicParsing -Method Post -Headers $script:headers -ContentType "text/xmlmc" -Body $script:body -ErrorAction:Stop -Proxy $script:ProxyURI -ProxyUseDefaultCredentials
            }
        } else {
            $r = Invoke-WebRequest -Uri $script:URI -UseBasicParsing -Method Post -Headers $script:headers -ContentType "text/xmlmc" -Body $script:body -ErrorAction:Stop
        }

        # Read and process response
        [XML]$script:xmlResponse = $r.Content
        $script:responseStatus = $script:xmlResponse.methodCallResult.status
        if(($script:responseStatus -eq "fail") -or ($script:responseStatus -eq "false")){
            $script:responseError = $script:xmlResponse.methodCallResult.state.error
        } else {
            $script:responseParams = $script:xmlResponse.methodCallResult.params
        }        
    }
    catch {
        # HTTP request failed - return exception in response
        $script:responseError = $_.Exception
        $script:responseStatus = "fail"
    }

    # Clear the XMLMC parameters now ready for the next API call
    Clear-HB-Params

    # Return an object of the results.
    $script:resultObject = New-Object PSObject -Property @{
        Status = $script:responseStatus
        Params = $script:responseParams
        Error = $script:responseError
    }
    # Return result object
    return  $script:resultObject       
}

# Export the functions available to the script importing this module
Export-ModuleMember -Function '*'