HornbillHelpers.psm1

##############################
# Hornbill Helpers Powershell Module
#
#.DESCRIPTION
# This module includes helper functions to retrieve useful information from your Hornbill instance, for use within your Azure Runbooks.
#
# Requires Powershell 3.0 or above, and the HornbillAPI Module to be installed on your Azure Automation Account
#
#.NOTES
# See example scripts and function documentation for guidance on usage.
##############################

##############################
#.SYNOPSIS
# Get the Graph Access Token
#
#.DESCRIPTION
# Returns the Graph Access Token required to create or update records.
#
#.PARAMETER TenantName
# The Service Name to search for
#
#.PARAMETER UserName
# The Username used to get the detail required to make the api call
#
#.PARAMETER Password
# The Passwird for the account retriving the api information
#
#.PARAMETER ClientID
# The ClientID of the auth space making the request
#
#.EXAMPLE
# New-HB-GraphAccessToken Will generate a new token allowing for the creation or updating of new or existing records
#
##############################
Function New-HB-GraphAccessToken {
    param (
        [string]$TenantName,
        [string]$UserName,
        [SecureString]$Password,
        [string]$ClientID
    )
    try {
        # Get installed Azure AD module
        $AzureADModules = Get-Module -Name "AzureAD" -ListAvailable -ErrorAction Stop -Verbose:$false

        if ($AzureADModules -ne $null) {
            # Check if multiple modules exist and determine the module path for the most current version
            if (($AzureADModules | Measure-Object).Count -gt 1) {
                $LatestAzureADModule = ($AzureADModules | Select-Object -Property Version | Sort-Object)[-1]
                $AzureADModulePath = $AzureADModules | Where-Object { $_.Version -like $LatestAzureADModule.Version } | Select-Object -ExpandProperty ModuleBase
            }
            else {
                $AzureADModulePath = $AzureADModules | Select-Object -ExpandProperty ModuleBase
            }

            try {
                # Load required assemblies
                $Assemblies = @(
                    (Join-Path -Path $AzureADModulePath -ChildPath "Microsoft.IdentityModel.Clients.ActiveDirectory.dll"),
                    (Join-Path -Path $AzureADModulePath -ChildPath "Microsoft.IdentityModel.Clients.ActiveDirectory.Platform.dll")
                )
                Add-Type -Path $Assemblies -ErrorAction Stop -Verbose:$true

                try {
                    $Authority = "https://login.microsoftonline.com/$($TenantName)/oauth2/v2.0/token"
                    # Construct new authentication context
                    $AuthenticationContext = New-Object -TypeName "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $Authority -ErrorAction Stop
                    
                    # Construct platform parameters
                    $PlatformParams = New-Object -TypeName "Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters" -ArgumentList "Auto" -ErrorAction Stop
                    
                    # Construct required identity model user password credential
                    $ADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential" -ArgumentList $UserName,$Password

                    try {
                        # Acquire access token
                        $GraphResource = "https://graph.microsoft.com"
                        $AuthenticationResult = ([Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($AuthenticationContext, $GraphResource, $ClientID, $ADCredential)).Result

                        # Check if access token was acquired
                        if ($AuthenticationResult.AccessToken -ne $null) {
                            Write-Output "Successfully acquired an access token for authentication"

                            # Construct authentication hash table for holding access token and header information
                            $Authentication = @{
                                "Content-Type" = "application/json"
                                "Authorization" = -join("Bearer ", $AuthenticationResult.AccessToken)
                                "ExpiresOn" = $AuthenticationResult.ExpiresOn
                            }     
                            return $Authentication       
                        } else {
                            Write-Error "Failure to acquire access token. Response from AcquireTokenAsync was null"
                            Break
                        }
                    } catch [System.Exception] {
                        Write-Error "An error occurred when attempting to call AcquireTokenAsync method. Error message: $($_.Exception.Message)"
                        Break
                    }
                } catch [System.Exception] {
                    Write-Error "An error occurred when constructing an authentication token. Error message: $($_.Exception.Message)"
                    Break
                    
                }
            } catch [System.Exception] {
                Write-Error "Unable to load required assemblies from AzureAD module to construct an authentication token. Error message: $($_.Exception.Message)"
                Break
            }
        } else {
            Write-Error "Azure AD PowerShell module is not present on this system, please install before you continue"
            Break
        }
    } catch [System.Exception] {
        Write-Error "Unable to load required AzureAD module to for retrieving an authentication token. Error message: $($_.Exception.Message)"
        Break
    }
}
##############################
#.SYNOPSIS
# Get the Primary Key value of a Service
#
#.DESCRIPTION
# Provide a Service Name, and this CMDLET will search your Hornbill instance for a matching record, returning the Primary Key
#
#.PARAMETER ServiceName
# The Service Name to search for
#
#.EXAMPLE
# Get-HB-ServiceID "Desktop Support" will search your services and return the primary key for the default language record
# of the service "Desktop Support"
#
##############################
#�Generate access token for Graph

function Get-HB-ServiceID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Service Name to retrieve its ID")]
        [ValidateNotNullOrEmpty()]
            [string]$ServiceName
    )

    Add-HB-Param       "application" "com.hornbill.servicemanager"
    Add-HB-Param       "queryName" "getServicesByField"
    Add-HB-Param       "maxResults" "1"
    Open-HB-Element    "queryParams"
    Add-HB-Param       "filter" $ServiceName
    Close-HB-Element   "queryParams"
    Open-HB-Element    "queryOptions"
    Add-HB-Param       "h_servicename" "true"
    Close-HB-Element   "queryOptions"
    Open-HB-Element    "queryOrder"
    Add-HB-Param       "column" "h_pk_serviceid"
    Add-HB-Param       "direction" "ascending"
    Close-HB-Element   "queryOrder"

    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "data" "queryExec"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        ServiceID = ""
        ExceptionName = ""
        ExceptionSummary = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        if(!$xmlmcOutput.params.rowData) {
            $resultObject.ExceptionName = "NoRecordsFound"
            $resultObject.ExceptionSummary = "No matching Service records found"
        } else {
            if($xmlmcOutput.params.rowData.row.h_servicename -ne $ServiceName) {
                $resultObject.ExceptionName = "Incorrect Record Returned"
                $resultObject.ExceptionSummary = "Service Name queried ["+$ServiceName+"] does not match Service returned ["+$xmlmcOutput.params.rowData.row.h_servicename +"]"
            } else {
                $resultObject.ServiceID = $xmlmcOutput.params.rowData.row.h_pk_serviceid
            }
        }

    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the Primary Key value of a Catalog Item
#
#.DESCRIPTION
# Provide a Catalog Item Name, and this CMDLET will search your Hornbill instance for a matching record, returning the Primary Key
#
#.PARAMETER CatalogItem
# The Catalog Item Name to search for
#
#.PARAMETER ServiceName
# The Service that the Catalog Item belogs to
#
#.PARAMETER RequestType
# The Request Type that the Catalog Item is configured for
#
#.EXAMPLE
# Get-HB-CatalogID "Support Me" "Desktop Support" "Incident" will search your services and return the primary key of
# the Catalog Item "Support Me", within the "Desktop Support" Service ans request type "Incident"
#
##############################
function Get-HB-CatalogID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Catalog Item Name to retrieve its ID")]
        [ValidateNotNullOrEmpty()]
            [string]$CatalogItem,
        [Parameter(Mandatory=$True, HelpMessage="Provide the Service Name to which the Catalog Item is associated")]
        [ValidateNotNullOrEmpty()]
            [string]$ServiceName,
        [Parameter(Mandatory=$True, HelpMessage="Provide the Request Type to which the Catalog Item is associated")]
        [ValidateNotNullOrEmpty()]
            [string]$RequestType
    )

    # Get service ID from Name
    $serviceId = 0
    $serviceObj = Get-HB-ServiceID $ServiceName
    if($serviceObj.ServiceID -gt 0) {
        $serviceId = $serviceObj.ServiceID
    }

    Add-HB-Param       "application" "com.hornbill.servicemanager"
    Add-HB-Param       "entity" "Catalogs"
    Open-HB-Element    "searchFilter"
    Add-HB-Param       "h_catalog_title" $CatalogItem
    Add-HB-Param       "h_service_id" $serviceId
    Add-HB-Param       "h_request_type" $RequestType
    Close-HB-Element   "searchFilter"
    Add-HB-Param       "maxResults" "1"

    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "data" "entityBrowseRecords"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        CatalogID = ""
        ExceptionName = ""
        ExceptionSummary = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        if(!$xmlmcOutput.params.rowData) {
            $resultObject.ExceptionName = "NoRecordsFound"
            $resultObject.ExceptionSummary = "No matching Catalog Item records found"
        } else {
            if($xmlmcOutput.params.rowData.row.h_catalog_title -ne $CatalogItem) {
                $resultObject.ExceptionName = "Incorrect Record Returned"
                $resultObject.ExceptionSummary = "Catalog Item Name queried ["+$CatalogItem+"] does not match Catalog Item returned ["+$xmlmcOutput.params.rowData.row.h_catalog_title +"]"
            } else {
                $resultObject.CatalogID = $xmlmcOutput.params.rowData.row.h_request_catalog_id
            }
        }

    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the Primary Key value of an Organisation
#
#.DESCRIPTION
# Provide a Organisation Name, and this CMDLET will search your Hornbill instance for a matching record, returning the Primary Key
#
#.PARAMETER OrgName
# The Organisation Name to search for
#
#.EXAMPLE
# Get-HB-OrganisationID "Support Me" will search your Organisations and return the primary key of the record found
#
##############################
function Get-HB-OrganisationID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Organisation Name to retrieve its ID")]
        [ValidateNotNullOrEmpty()]
            [string]$OrganisationName
    )

    Add-HB-Param       "application" "com.hornbill.core"
    Add-HB-Param       "entity" "Organizations"
    Open-HB-Element    "searchFilter"
    Add-HB-Param       "h_organization_name" $OrganisationName
    Close-HB-Element   "searchFilter"
    Add-HB-Param       "maxResults" "1"

    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "data" "entityBrowseRecords"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        OrganisationID = ""
        ExceptionName = ""
        ExceptionSummary = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        if(!$xmlmcOutput.params.rowData) {
            $resultObject.ExceptionName = "NoRecordsFound"
            $resultObject.ExceptionSummary = "No matching Organisation records found"
        } else {
            if($xmlmcOutput.params.rowData.row.h_organization_name -ne $OrganisationName) {
                $resultObject.ExceptionName = "Incorrect Record Returned"
                $resultObject.ExceptionSummary = "Organisation Name queried ["+$OrganisationName+"] does not match Organisation returned ["+$xmlmcOutput.params.rowData.row.h_organization_name +"]"
            } else {
                $resultObject.OrganisationID = $xmlmcOutput.params.rowData.row.h_organization_id
            }
        }

    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the Primary Key value of a Priority
#
#.DESCRIPTION
# Provide a Priority Name, and this CMDLET will search your Hornbill instance for a matching record, returning the Primary Key
#
#.PARAMETER PriorityName
# The Priority Name to search for
#
#.EXAMPLE
# Get-HB-PriorityID "Low" will search your priorities and return the primary key for "Low" priority
#
##############################
function Get-HB-PriorityID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Priority Name to retrieve its ID")]
        [ValidateNotNullOrEmpty()]
            [string]$PriorityName
    )

    Add-HB-Param       "application" "com.hornbill.servicemanager"
    Add-HB-Param       "entity" "Priority"
    Open-HB-Element    "searchFilter"
    Add-HB-Param       "h_priorityname" $PriorityName
    Close-HB-Element   "searchFilter"
    Add-HB-Param       "maxResults" "1"
    
    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "data" "entityBrowseRecords"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        PriorityID = ""
        ExceptionName = ""
        ExceptionSummary = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        if(!$xmlmcOutput.params.rowData) {
            $resultObject.ExceptionName = "NoRecordsFound"
            $resultObject.ExceptionSummary = "No matching Priority records found"
        } else {
            if($xmlmcOutput.params.rowData.row.h_priorityname -ne $PriorityName) {
                $resultObject.ExceptionName = "Incorrect Record Returned"
                $resultObject.ExceptionSummary = "Priority Name queried ["+$PriorityName+"] does not match Priority returned ["+$xmlmcOutput.params.rowData.row.h_priorityname +"]"
            } else {
                $resultObject.PriorityID = $xmlmcOutput.params.rowData.row.h_pk_priorityid
            }
        }

    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the Primary Key value of a Site
#
#.DESCRIPTION
# Provide a Site Name, and this CMDLET will search your Hornbill instance for a matching record, returning the Primary Key
#
#.PARAMETER SiteName
# The Site Name to search for
#
#.EXAMPLE
# Get-HB-SiteID "Hull" will search your sites and return the primary key for the "Hull" site
#
##############################
function Get-HB-SiteID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Site Name to retrieve its ID")]
        [ValidateNotNullOrEmpty()]
            [string]$SiteName
    )

    Add-HB-Param        "application" "com.hornbill.core"
    Add-HB-Param        "entity" "Site"
    Open-HB-Element     "searchFilter"
    Add-HB-Param        "h_site_name" $SiteName
    Close-HB-Element    "searchFilter"
    Open-HB-Element     "orderBy"
    Add-HB-Param        "column" "h_site_name"
    Add-HB-Param        "direction" "ascending"
    Close-HB-Element    "orderBy"
    Add-HB-Param        "maxResults" "1"
    
    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "data" "entityBrowseRecords"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        SiteID = ""
        ExceptionName = ""
        ExceptionSummary = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        if(!$xmlmcOutput.params.rowData) {
            $resultObject.ExceptionName = "NoRecordsFound"
            $resultObject.ExceptionSummary = "No matching Site records found"
        } else {
            if($xmlmcOutput.params.rowData.row.h_site_name -ne $SiteName) {
                $resultObject.ExceptionName = "Incorrect Record Returned"
                $resultObject.ExceptionSummary = "Site Name queried ["+$SiteName+"] does not match Site returned ["+$xmlmcOutput.params.rowData.row.h_site_name +"]"
            } else {
                $resultObject.SiteID = $xmlmcOutput.params.rowData.row.h_id
            }
        }

    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the Activity Stream ID of a Workspace
#
#.DESCRIPTION
# Provide a Workspace Name, and this CMDLET will search your Hornbill instance for a matching record, returning the Activity Stream ID
#
#.PARAMETER WorkspaceName
# The Workspace Name to search for
#
#.EXAMPLE
# Get-HB-WorkspaceID "Demo Workspace" will search your Workspaces and return the Activity Stream ID for the "Demo Workspace" workspace
#
##############################
function Get-HB-WorkspaceID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Workspace Name to retrieve its Activity Stream ID")]
        [ValidateNotNullOrEmpty()]
            [string]$WorkspaceName
    )

    Add-HB-Param "displayName" $WorkspaceName
    
    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "activity" "workspaceLookup"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        ActivityStreamID = ""
    }

    if($xmlmcOutput.status -eq "ok"){
        $resultObject.ActivityStreamID = $xmlmcOutput.params.activityStreamID   
    }
    return $resultObject
}


##############################
#.SYNOPSIS
# Retrieves request details
#
#.DESCRIPTION
# Retrieves details about a Request
#
#.PARAMETER RequestRef
# The Request reference
#
#.EXAMPLE
# Get-HB-Request "IN00000123"
#
###############################
function Get-HB-Request {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the Request Reference to retrieve its details")]
        [ValidateNotNullOrEmpty()]
            [string]$RequestRef
    )

    Add-HB-Param "requestId" $RequestRef
    
    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "apps/com.hornbill.servicemanager/Requests" "smGetDetails"
    $resultObject = New-Object PSObject -Property @{
        Status           = $xmlmcOutput.status
        Error            = $xmlmcOutput.error
        RequestDetails   = ""
        RequestRelated   = ""
        RequestBPM       = ""
        ExceptionName    = ""
        ExceptionSummary = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        if(!$xmlmcOutput.params) {
            $resultObject.ExceptionName = "NoRecordsFound"
            $resultObject.ExceptionSummary = "No matching Request record found"
        } else {
            $resultObject.RequestDetails = $xmlmcOutput.params.requestDetails
            if($xmlmcOutput.params.requestRelatedDetails){
                $resultObject.RequestRelated = $xmlmcOutput.params.requestRelatedDetails
            }
            if($xmlmcOutput.params.requestBPM){
                $resultObject.RequestBPM = $xmlmcOutput.params.requestBPM
            }
        }

    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the details of a Hornbill User
#
#.DESCRIPTION
# Provide a User Name, and this CMDLET will search your Hornbill instance for a matching record, returning details
#
#.PARAMETER UserName
# The User Name to search for
#
#.EXAMPLE
# Get-HB-User "some@ad.username.com" will search your Users and return the user object for the "some@ad.username.com" user
#
##############################
function Get-HB-User {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the User ID to retrieve its details")]
        [ValidateNotNullOrEmpty()]
            [string]$UserName
    )
    # Build XMLMC call
    Add-HB-Param    "userId" $UserName
    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "admin" "userGetInfo"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
    }
    if($xmlmcOutput.status -eq "ok"){
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "UserType" -Value $xmlmcOutput.params.userType
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "Name" -Value $xmlmcOutput.params.name
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "FirstName" -Value $xmlmcOutput.params.firstName
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "JobTitle" -Value $xmlmcOutput.params.jobTitle
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "HomeOrganization" -Value $xmlmcOutput.params.homeOrganization
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "Site" -Value $xmlmcOutput.params.site
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "Phone" -Value $xmlmcOutput.params.phone
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "Email" -Value $xmlmcOutput.params.email
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "Mobile" -Value $xmlmcOutput.params.mobile
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "AvailabilityStatus" -Value $xmlmcOutput.params.availabilityStatus
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "LastLogonTime" -Value $xmlmcOutput.params.lastLogonTime
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "OnlineStatus" -Value $xmlmcOutput.params.onlineStatus
        Add-Member -InputObject $resultObject -MemberType NoteProperty -Name "AccountStatus" -Value $xmlmcOutput.params.accountStatus
    }
    return $resultObject
}

##############################
#.SYNOPSIS
# Get the ID of a Hornbill asset
#
#.DESCRIPTION
# Provide a string to query, the entity to search against, and the column to search within, and this CMDLET will search your Hornbill instance
# for a matching asset, returning its primary key
#
#.PARAMETER Value
# The Value to search for
#
#.PARAMETER Entity
# The Entity to search within
#
#.PARAMETER Column
# The Column to search within
#
#.EXAMPLE
# Get-HB-AssetID "someserialnumber" "AssetsMobileDevice" "h_serial_number" will search your Hornbill Mobile Assets and
# return the ID for asset with "someserialnumber" in the serial number field
#
##############################
function Get-HB-AssetID {
    Param(
        [Parameter(Mandatory=$True, HelpMessage="Provide the value to search against")]
        [ValidateNotNullOrEmpty()]
            [string]$Value, 

        [Parameter(Mandatory=$True, HelpMessage="Provide the asset entity name to search")]
        [ValidateNotNullOrEmpty()]
            [string]$Entity, 

        [Parameter(Mandatory=$True, HelpMessage="Provide the asset entity column to search")]
        [ValidateNotNullOrEmpty()]
            [string]$Column
    )
    # Build XMLMC call
    Add-HB-Param        "application" "com.hornbill.servicemanager"
    Add-HB-Param        "entity" $Entity
    Open-HB-Element     "searchFilter"
    Add-HB-Param        "column" $Column
    Add-HB-Param        "value" $Value
    Add-HB-Param        "matchType" "exact"
    Close-HB-Element    "searchFilter"
    Add-HB-Param        "maxResults" "1"

    # Invoke XMLMC call, output returned as PSObject type
    $xmlmcOutput = Invoke-HB-XMLMC "data" "entityBrowseRecords2"
    $resultObject = New-Object PSObject -Property @{
        Status = $xmlmcOutput.status
        Error = $xmlmcOutput.error
        AssetID = ""
    }
    if($xmlmcOutput.status -eq "ok"){
        $resultObject.AssetID = $xmlmcOutput.params.RowData.Row.h_pk_asset_id
    }
    return $resultObject
}

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