Configuration Script Service Manager SMA System Center

SCSM – Passing Attachments via Web Service e.g. SMA Web Service

If you do automation in a “Microsoft World” you probably start these days with Service Manager and Service Management Automation (SMA). At one point you will have a need for building a connector to another system for exchanging data. One example could be that you need to submit incident data from Service Manager to another ticket system e.g. ServiceNow. If you are lucky the target system offers a web service and you just need to pass the data in XML. That sounds pretty easy but how are you going to pass the attachments to the foreign system via web service? One approach is to get the attachments from the source, save it as files on a file share and then the files are pushed to or pulled from the target system. Yes this is one way, but why should you make this extra step and saving the attachment files on a file share? Isn’t it possible just passing the data in one shot? Well, this is the purpose of this blog post.

I don’t have access to ServiceNow nor another ticketing system which would offer a web service interface. For that reason I will use the SMA web service to show how to convert the attachment and passing it via string and XML data.

For a better understanding I created a graphic…

image

The first runbook is called Get-Attachment and has an input parameter WorkItemID. The runbook connects to Service Manager and iterates through the attachments of the specified work item in this example IR1234 and converts it to a Base64String. The last line of Get-Attachment runbook uses the Start-SmaRunbook cmdlet to call the Add-Attachment runbook. The Start-SmaRunbook cmdlet uses the SMA web service to trigger the runbook and submit the attachment data.  This is the step which proofs, that you can submit the attachment data through a web service call.

The Add-Attachment runbook uses the “XML” output string from Get-Attachment runbook and converts it to XML. The actual attachment content is converted back from the Base64String and finally added as attachment to the work item.

Well, yes it adds the attachments to the same work item where it got the items from (IR1234), but the purpose of this post is to show the round trip of the attachments. It does not make sense in a real-world scenario I know, again it is just to show how to solve this problem technically.

There are few posts on the internet which show how to export the attachments from Service Manager. One example is provided by the guys from Litware, you can find it here. I used some of this code to retrieve the attachments from the work item. But instead of saving the attachments to the file system, the content is converted to a Base64String.

Workflow Get-Attachment:

workflow Get-Attachment
{

[OutputType([string[]])]
Param
(
        #Get the work item ID
        [Parameter(Mandatory=$true)][string]$WorkItemID
)
            #Define the web service endpoint, target SCSM & credentials in SMA (Assets)
            $WebServiceEndpoint = Get-AutomationVariable -Name "VARG-SMAWebServiceEndPoint"
            $SCSMServer = Get-AutomationVariable -Name "VARG-SCSMServer"
            $Creds = Get-AutomationPSCredential -Name "VARG-SCSMServerCredential"
            #Run the next code as InlineScript on Service Manager
            [string]$XMLString = InlineScript 
            {
                #Define classes and realtionships
                $ClassWorkItem = Get-SCSMClass -Name "System.WorkItem$"
                $ClassWIhasAttachment = Get-SCSMRelationshipClass -Name "System.WorkItemHasFileAttachment"
                $WorkItemObj = Get-SCSMObject -Class $ClassWorkItem -Filter "Id -eq $Using:WorkItemID"
                $Attachments = Get-SCSMRelatedObject -SMObject $WorkItemObj -Relationship $ClassWIhasAttachment

                $XMLString = ""
                $Content =""
                $Content= @()
                #If the incident contains attachments
                If($Attachments -ne $Null)
                {
                        #Iterate thorough each attachment and "convert" it into a memory stream
                        ForEach ($Attachment in $Attachments)
                        {      
                                $MemoryStream = New-Object IO.MemoryStream
                                $Buffer = New-Object byte[] 8192
                                [int]$BytesRead | Out-Null
                                while (($BytesRead = $Attachment.Content.Read($Buffer, 0, $Buffer.Length)) -gt 0)
                                {
                                    $MemoryStream.Write($Buffer, 0, $BytesRead)
                                } 

                                $Memory = $MemoryStream.Toarray() 

                        #Convert the bytes (attachments) in memory to a Base64String                          
                        $Content = [convert]::ToBase64String($Memory)

#This here-string is used to concatenate the data in a string format for passing it to the next runbook via web service                     
$XMLString +=@"
<Attachment>
<DisplayName>$($Attachment.DisplayName)</DisplayName>
<Description>$($Attachment.Description)</Description>
<Extension>$($Attachment.Extension)</Extension>
<Content>$Content</Content>
</Attachment>
"@
                            #Set memory stream to Null
                            $MemoryStream.Close()
                            $Memory = $Null
                        }
                 #Return the previously built here-string with all the attachment data     
                 Return $XMLString
             }         
         }    -PSComputerName $SCSMServer -PSCredential $Creds   
    #Start the SMA runbook via Start-SMARunbook cmdlet, which uses the SMA webservice to pass the data
    Start-SmaRunbook -WebServiceEndpoint  $WebServiceEndpoint -Name "Add-Attachment" -Parameters @{"XMLString"=$XMLString;"WorkItemID"=$WorkItemID}
}

The next step is adding the attachments to the work item. There are also few sources on the internet which discuss this topic, but not many really show how to do it. The best “hint” I found was this post here on TechNet forum . To complete our solution I needed to modify the code accordingly. Basically I create an XML object from the $XMLString input containing all the information of the attachments. The attachment content itself is converted back from the Base64String and finally used to create and relate the attachment object.

Workflow Add-Attachment:

workflow Add-Attachment
{
Param
(
        #Pass attachment data and work item ID
        [Parameter(Mandatory=$true)][string]$XMLString,
        [Parameter(Mandatory=$true)][string]$WorkItemID
)
        #Get the SMA automation variable (Asset)
        $SCSMServer = Get-AutomationVariable -Name "VARG-SCSMServer"
        $Creds = Get-AutomationPSCredential -Name "VARG-SCSMServerCredential"
        #Run the next code as InlineScript remotely on the SCSM server
        InlineScript
        {
            #Define the classes and relationships
            $FileAttachmentRel = Get-SCSMRelationshipClass "System.WorkItemHasFileAttachment"
            $FileaAttachmentClass = Get-SCSMClass -Name "System.FileAttachment"
            $WorkItemProjection = Get-SCSMObjectProjection System.WorkItem.Projection -Filter "id -eq $Using:WorkItemID"
            $ManagementGroup = New-Object Microsoft.EnterpriseManagement.EnterpriseManagementGroup $Using:SCSMServer
            #Embedd the $XMLString into <Attachments></Attachments> nodes and convert it to XML
            [xml]$XML = "<Attachments>$($Using:XMLString)</Attachments>"
                #Iterate through each attachment node and get the properties
                ForEach ($Attachment in $XML.Attachments.Attachment)
                {
                    #Convert the Base64String back to bytes
                    $AttachmentContent = [convert]::FromBase64String($Attachment.Content)
                    #Create a new MemoryStream object out of the attachment data
                    $MemoryStream = New-Object System.IO.MemoryStream($AttachmentContent,0,$AttachmentContent.length)
                    #Create the attachment object itself and adding the attachment properties from the received XML
                    $NewFile = new-object Microsoft.EnterpriseManagement.Common.CreatableEnterpriseManagementObject($ManagementGroup, $FileaAttachmentClass)
                    $NewFile.Item($FileaAttachmentClass, "Id").Value = [Guid]::NewGuid().ToString()
                    $NewFile.Item($FileaAttachmentClass, "DisplayName").Value = $Attachment.DisplayName
                    $NewFile.Item($FileaAttachmentClass, "Description").Value = $Attachment.Description
                    $NewFile.Item($FileaAttachmentClass, "Extension").Value = 	$Attachment.Extension
                    $NewFile.Item($FileaAttachmentClass, "Size").Value = 		$MemoryStream.Length
                    $NewFile.Item($FileaAttachmentClass, "AddedDate").Value = 	[DateTime]::Now.ToUniversalTime()
                    $NewFile.Item($FileaAttachmentClass, "Content").Value = 	$MemoryStream
                    #Add the attachment to the work item and commit the changes
                    $WorkItemProjection.__base.Add($NewFile, $FileAttachmentRel.Target)
                    $WorkItemProjection.__base.Commit()
                }
        } -PSComputerName $SCSMServer -PSCredential $Creds   
} 

It was a bit tricky to figure everything out but now I hope providing this example helps you automating your environment. Of course if you need to build e.g. a ServiceNow SOAP request you would have to construct the XML according to the ServiceNow SOAP specification.

I uploaded the PowerShell scripts to TechNet Gallery here.

One Reply to “SCSM – Passing Attachments via Web Service e.g. SMA Web Service

  1. Hi! Sorry for a long shot on an old post, but here goes. We’re doing pretty much the same thing as you’re doing here in our system (but with the first runbook sending the data to a third party and the same third party sending other files back to SMA), but we’re having an issue when the files get too large. When the message sent to SMA is about a MB and upwards, the runbook won’t even start. The runbook worker service logs an error in the event log about a closing pipe and then no part of the runbook is run. The error in SMA says that the action “activate” can’t be run.
    Is this something you’ve seen, or did your setup handle large files as well?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.