Background
In our environment we deploy computers with Symantec's Ghost Solution Suite, and then maintain them through the rest of their lifecycle using ITMS. One of the issues we faced recently in our Windows 10 migrations were machines post-migration still recieving (for a short time) their old Windows 7 software polcies. To resolve, we decided that as part of our migrations that the SMP object should be deleted.
In the past we used the ASDK binaries for this. However, Symantec decided after ITMS 8.0 to not maintain these anymore (although the 8.0 versions do seem to work on 8.5). On contacting Symantec Support we were advised that these binaries were no longer supported, and that customers should now call the SMP's ASDK webservices directly. This article contains the script we created for this.
As there aren't very many examples on using the ASDK around, I hope that this script can also be instructive to others to demonstrate how the ASDK can be used from simple VBScripts.
If this script seems daunting -please don't be put off. You can actually fire off ASDK calls in a few lines. The bulk of it is really the logging and checks we add when moving a prototype into production.
Pre-Requisites
- To run this script you will need to have ChillKat to be installed on the machine running the script. This is a pretty cheap cross-platform toolkit, so it shouldn't break the bank. The reason why I'm using ChillKat is for security -it enables VBScript webservice calls to use Windows Integrated Auth, which in turn means that I didn't have to expose the SMP access credentials in the script.
- The ADSK should be installed on the SMP.
This script should work across all ITMS server versions as the ADSK webservices called are very basic.
What if I don't have ChillKat
If you don't have, or don't want to use ChillKat, I've left my legacy functions in the script. The legacy functions accept the SMP credentials in plain text, and these can be called in place of the ChillKat functions. You can use Chillcat free for 30 days -just install the 32-bit and 64-bit MSIs, then off you go!
What Does the Script Do
The script executes the following tasks to perform a computer delete,
- Contacts "http://SMP/altiris/ASDK.NS/ItemManagementService.asmx/GetItemsByName?" to get data on the computer (which we know by name)
- Extracts from the XML returned the computer's GUID
- Contacts "http://SMP/altiris/ASDK.NS/ItemManagementService.asmx/DeleteItem?" to delete the computer
- Contacts "http://SMP/altiris/ASDK.NS/ItemManagementService.asmx/GetItemsByName?" to confirm that the computer has been deleted
For robustness, the script will re-try every SMP request, upto 10 times. This should safely accomodate most transient network glitches. It should of course work on the first attempt, but I can spare a few lines of code to be doubly sure.
Using the Script
The script is designed to be executed from a GSS Server. You'll notice in the user editable section the following lines,
'--------------------------------- ' START OF USER EDITABLE VARIABLES '--------------------------------- StrNS="PUT.YOUR.SMP.DNS.ADDRESS.HERE" sLogPath="C:\Program Files (x86)\Altiris\eXpress\Deployment Server\Logs" iRetry=10 iRetryPause=30000 '--------------------------------- ' END OF USER EDITABLE VARIABLES '-------------------------------
These are the lines you might want to tweak to tune the script to your environment. For sure, you'll want to set StrNS to the DNS address of your SMP server. If you are running this through Ghost Solution Suite, then the log path should be fine for you. But feel free to meddle for your testing.
Immediately after the script extract above, you'll see the following line,
StrComp="%COMPNAME%"
This variable is injected by Ghost Solution Suite at runtime. For testing though, you might want to initially override this with an actual computer name, such as,
StrComp="myOldPC"
And that's it. From this point, just save the script and then run it,
In the case, the script found that the computer "myOldPC" didn't exist so displayed this info message (this is the line that's also written to the log file by the way).
When a computer is successfully deleted you'll see this magic,
And this took a couple of seconds. Notice in all this that you didn't have to enter any credentials for this. All of this executes with the credentials the command prompt is running under.
Creating Your GSS Job
Creating the job in GSS is simple,
- Create a New Job.
Let's call it "Delete Computer From the SMP"
- Add a "Run Script" task.
Embed this vbscript from this article.
- Script Run Location settings
Set "Script Run Location" to "Locally on the Ghost Solution Suite". Uncheck the box "Run when the agent is connected" (this is so you can it even when the computer is offline).
- Set the 99 Return Code
As the script exits with exit code 99 when the object can't be located in the SMP for deletion, we need to have this set as a success code. Success because the object isn't there now (which is what we want), but we might want to know that it wasn't there in the first place.
And that's it. Give it a whirl.
Feedback
As always, feel free to give feedback good and bad. It's constructive either way, and quite often I learn something new.
Appdendix -VBScript
Including the VBScript inline in plain text for googlability.
'vbscript 'Function: Remove computer from SMP : 'Author: Ian Atkin, 25th Jan 2015 'Overview: Using the computer name, the script calls the SMP webservices to get the computer's GUID, ' and with this we call the webservices again with a delete. ''Notes: This script is intended be embedded in a GSS task which is executed FROM THE SERVER. Dim StrComp,StrNS Dim sLogfile Dim iRetry '--------------------------------- ' Setup Error Handler '--------------------------------- ' This class makes sure that runtime errors are caught ' and returned as ERRORLEVEL when the script exits Class QuitErrorlevelOnRuntimeError Private Sub Class_Terminate() ' When 'WScript.Quit' is called, it seems to set err.Number ' to -2147155971. Since we want the quit to return it's ' error code, don't return err.number if this err.number is ' seen dim quitErrNum : quitErrNum = -2147155971 if Err.Number <> 0 and Err.Number <> quitErrNum then WScript.Quit Err.Number end if End Sub End Class Dim qeloreObj : Set qeloreObj = New QuitErrorlevelOnRuntimeError '--------------------------------- ' START OF USER EDITABLE VARIABLES '--------------------------------- StrNS="PUT.YOUR.SMP.DNS.ADDRESS.HERE" sLogPath="C:\Program Files (x86)\Altiris\eXpress\Deployment Server\Logs" iRetry=10 iRetryPause=30000 '--------------------------------- ' END OF USER EDITABLE VARIABLES '--------------------------------- StrComp="%COMPNAME%" sLogFile=sLogPath & "\SMP_Delete.log" sErrorFile=sLogPath & "\SMP_Delete_Error_"& StrComp & ".log"'--------------------------------- ' Make sure the full path to the log has been built '--------------------------------- Call BuildFullPath(sLogPath) '--------------------------------- ' Get inventory data from computer name (it includes the GUID) '--------------------------------- For i=1 to iRetry StrCompXML=ChillKatGetCompXML(StrComp,StrNS) if left(StrCompXML,5)="ERROR" then myError=1 wscript.sleep iRetryPause else myError=0 Exit For end if Next if myError=1 then writelog "ERROR: Cannot get data from SMP for "& StrComp & ". Tried "& i & " times. For details, see "& SErrorFile call writefile(SErrorFile,StrCompXML) wscript.quit 1 end if if instr(StrCompXML,"<Guid>")=0 then writelog "INFO: Computer "& StrComp & " doesn't exist in the SMP. Nothing to do." wscript.quit 99 end if '--------------------------------- ' From inventory XML, extract the GUID) '--------------------------------- StrGuid=GetElementFromXML(StrCompXML,"Guid") if StrGuid="ERROR" then writelog "ERROR: Cannot extract Guid from XML recieved from SMP. For XML, see "& SErrorFile call writefile(SErrorFile,StrCompXML) wscript.quit 1 end if For i=1 to iRetry '--------------------------------- ' Delete computer object using GUID '--------------------------------- result=ChillKatDeleteComputer(StrGuid,StrNS) '--------------------------------- ' Confirm Computer has been deleted. '--------------------------------- StrCompXML=ChillKatGetCompXML(StrComp,StrNS) if left(StrCompXML,5)="ERROR" then myError=1 wscript.sleep iRetryPause else if instr(StrCompXML,"<Guid>")=0 then 'Cannot fund GUID entry in XML, so computer is now deleted. myError=0 Exit For end if end if Next if myError=0 then writelog "SUCCESS: Computer "& StrComp & " has been deleted from the SMP. Tried "& i & " time(s)." wscript.quit else writelog "ERROR: Computer "& StrComp & " has not been deleted from the SMP. Tried "& i & " times. For XML details, see "& SErrorFile call writefile(SErrorFile,StrCompXML) wscript.quit 1 end if wscript.quit '------------------------------------------------------ ' FUNCTIONS AND SUBS '------------------------------------------------------ Function GetElementFromXML(myXML,myElement) 'returns requested element from XML Set doc = CreateObject("MSXML2.DOMDocument") doc.loadXML(myXML) Set nodes = doc.getElementsByTagName(myElement) GetElementFromXML="ERROR" For Each node In nodes GetElementFromXML=node.text Next End Function Sub BuildFullPath(ByVal FullPath) Set fso = CreateObject("Scripting.FileSystemObject") If Not fso.FolderExists(FullPath) Then BuildFullPath fso.GetParentFolderName(FullPath) fso.CreateFolder FullPath End If End Sub '______________________________________________________ ' CHILLKAT WEBSERVICE FUNCTIONS '¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Function ChillKatGetCompXML(myComp,myNS) 'This function calls the webservice securely using NTLM Logged in user authentication 'Chilkat implements the NTLM protocol directly, which is how it can be used on non-Windows systems. 'However, Chilkat can also optionally use Microsoft's SSPI (see https://docs.microsoft.com/en-us/windows/desktop/secauthn/ssp-packages-p...) 'to allow for Windows Integrated Authentication. set http = CreateObject("Chilkat_9_5_0.Http") ' Any string unlocks the component for the 1st 30-days. success = http.UnlockComponent("Anything for 30-day trial") If (success <> 1) Then outFile.WriteLine(http.LastErrorText) WScript.Quit End If 'Setting the HTTP Login equal to the empty string, and the Password equal to the keyword "default" 'will cause Chilkat to use the Microsoft SSPI w/ integrated authentication. http.Login="" http.Password="default" 'We can also explicitly indicate that NTLM or Negotiate authentication is to be used: http.NegotiateAuth = 1 strRequest="itemName="& myComp html= http.QuickGetStr("https://"& myNS & "/altiris/ASDK.NS/ItemManagementService.asmx/GetItemsByName?"& strRequest) If (http.LastMethodSuccess <> 1) Then ChillKatGetCompXML="ERROR: "& http.LastErrorText Else ChillKatGetCompXML=html End If End Function Function ChillKatDeleteComputer(myGuid,myNS) strRequest="itemGuid="& "{"& StrGuid & "}" set http = CreateObject("Chilkat_9_5_0.Http") http.Login="" http.Password="default" http.NegotiateAuth = 1 html= http.QuickGetStr("https://"& myNS & "/altiris/ASDK.NS/ItemManagementService.asmx/DeleteItem?"& strRequest) If (http.LastMethodSuccess <> 1) Then ChillKatDeleteComputer="ERROR: "& http.LastErrorText Else ChillKatDeleteComputer=html End If End Function '______________________________________________________ ' FILE FUNCTIONS '¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ function WriteFile(FileName, Contents) Dim OutStream, FS on error resume Next Set FS = CreateObject("Scripting.FileSystemObject") Set OutStream = FS.OpenTextFile(FileName, 2, True) OutStream.Write Contents End Function Sub WriteLog(sTxt) '-Subroutine to Write Entries to the LogFile Dim sFile, fso, ts Set fso = CreateObject("Scripting.FileSystemObject") Set ts = fso.OpenTextFile(sLogFile, 8, True) ts.WriteLine Now() & " -"& sTxt ts.close Set ts = Nothing Set fso = Nothing If InStr(1, WScript.FullName, "cscript", vbTextCompare) Then WScript.Echo sTxt End if End Sub '______________________________________________________ ' EMAIL FUNCTIONS '¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Function fSendSMTPMail(sTo, sFrom, sSubject, sBody, sAttach, iPriority, sSMTPServer) '# sTo is comma or semi-colon delimited. 'On Error Resume Next Const cdoSendUsingMethod = "http://schemas.microsoft.com/cdo/configuration/sendusing" Const cdoSMTPServer = "http://schemas.microsoft.com/cdo/configuration/smtpserver" Dim objMsg, objConf, objConfFields, fso, sAttachText Set fso = CreateObject("Scripting.FileSystemObject") Set objMsg = CreateObject("CDO.Message") Set objConf = CreateObject("CDO.Configuration") Set objConfFields = objConf.Fields With objConfFields .Item(cdoSendUsingMethod) = 2 .Item(cdoSMTPServer) = sSMTPServer .Update End With If sAttach <> "" Then If fso.FileExists(sAttach) Then objMsg.AddAttachment sAttach 'sBody = "-= File '"& sAttach & "' contents below: =-"& vbCrlf & vbCrlf 'sAttachText = fso.OpenTextFile(sAttach, 1).ReadAll 'sBody = sBody & sAttachText Else sBody = "-= ERROR: Unable to attach '"& sAttach & "' - file cannot be found. =-"& vbCrlf & vbCrlf & sBody End If End If '## Separate multiple recipients using either comma or semi-colon. With objMsg Set .Configuration = objConf .To = sTo .From = sFrom .Subject = sSubject .TextBody = sBody .Fields("urn:schemas:httpmail:importance").Value = iPriority .Fields.Update End With If Err.Number = 0 Then objMsg.Send fSendSMTPMail = 0 Else fSendSMTPMail = Err.Number End If Set objConf = Nothing Set objMsg = Nothing End Function '______________________________________________________ ' MICROSOFT (LEGACY) WEBSERVICE FUNCTIONS '¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ 'The functions here are no longer in use and are left for reference only. 'Now using Chillkat in preference as they permit integrated Windows security Function DeleteComputer(myGuid,myNS,myUser,myPassword) 'USAGE result=DeleteComputer(StrGuid,StrNS,StrUser,StrPassword) strRequest="itemGuid="& "{"& StrGuid & "}" set http=createObject("Microsoft.XMLHTTP") http.open "GET","http://"& myNS & "/altiris/ASDK.NS/ItemManagementService.asmx/DeleteItem?"& strRequest,false,myUser,myPassword http.send If http.Status = 200 Then DeleteComputer=http.responseText else DeleteComputer="ERROR: "& http.status End If End Function Function GetCompXML(myComp,myNS,myUser,myPassword) 'USAGE: StrCompXML=GetCompXML(StrComp,StrNS,StrUser,StrPassword) dim http dim strRequest set http=createObject("Microsoft.XMLHTTP") strRequest="itemName="& myComp http.open "GET","http://"& myNS & "/altiris/ASDK.NS/ItemManagementService.asmx/GetItemsByName?"& strRequest,false,myUser,myPassword http.setRequestHeader "Content-Type","text/xml" http.send If http.Status = 200 Then GetCompXML=http.responseText else GetCompXML="ERROR: "& http.status End If End Function