Ask a questionAsk a question
 

AnswerSDK - Sample Code - Computer Import - Newbie

  • Wednesday, June 04, 2008 3:05 PMChris901 Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

     

    Hi,

     

    Whilst having worked with SMS/SCCM from an Infrastructure perspective for a while, I am now having to delve deaper into the bowels of SCCM!!

     

    I am putting together a SCCM OSD deployment process, part of which I want to include scriptinig the importing of the computer object along with a number of variables to be used within the OSD process.  Delving into the SDK I came accross two pieces of code that would appear to be helpful in providing this functionality.

     

    Taken from:

     

    How to Connect to an SMS Provider in Configuration Manager by Using WMI which I am presuming connects to the SMS Provider

     

    Dim connection
    Dim computer
    Dim userName
    Dim userPassword
    Dim password 'Password object

    On Error Resume Next

    Wscript.StdOut.Write "Computer you want to connect to (Enter . for local): "
    computer = WScript.StdIn.ReadLine

    If computer = "." Then
        userName = ""
        userPassword = ""
    Else
        Wscript.StdOut.Write "Please enter the user name: "
        userName = WScript.StdIn.ReadLine
       
        Set password = CreateObject("ScriptPW.Password")
        WScript.StdOut.Write "Please enter your password:"
        userPassword = password.GetPassword()
    End If
         
    Set connection = Connect(computer,userName,userPassword)

    If Err.Number<>0 Then
        Wscript.Echo "Call to connect failed"
    End If

    Call SNIPPETMETHODNAME (connection)

    Sub SNIPPETMETHODNAME(connection)
       ' Insert snippet code here.
    End Sub

    Function Connect(server, userName, userPassword)
       
        On Error Resume Next
       
        Dim net
        Dim localConnection
        Dim swbemLocator
        Dim swbemServices
        Dim providerLoc
        Dim location
       
        Set swbemLocator = CreateObject("WbemScripting.SWbemLocator")

        swbemLocator.Security_.AuthenticationLevel = 6 'Packet Privacy
       
        ' If  the server is local, don't supply credentials.
        Set net = CreateObject("WScript.NetWork")
        If UCase(net.ComputerName) = UCase(server) Then
            localConnection = true
            userName = ""
            userPassword = ""
            server = "."
        End If
       
        ' Connect to the server.
        Set swbemServices= swbemLocator.ConnectServer _
                (server, "root\sms",userName,userPassword)
        If Err.Number<>0 Then
            Wscript.Echo "Couldn't connect: " + Err.Description
            Connect = null
            Exit Function
        End If
       

        ' Determine where the provider is and connect.
        Set providerLoc = swbemServices.InstancesOf("SMS_ProviderLocation")

            For Each location In providerLoc
                If location.ProviderForLocalSite = True Then
                    Set swbemServices = swbemLocator.ConnectServer _
                     (location.Machine, "root\sms\site_" + _
                        location.SiteCode,userName,userPassword)
                    If Err.Number<>0 Then
                        Wscript.Echo "Couldn't connect:" + Err.Description
                        Connect = Null
                        Exit Function
                    End If
                    Set Connect = swbemServices
                    Exit Function
                End If
            Next
        Set Connect = null ' Failed to connect.
    End Function

     

    Second piece to import the computer

     

    Sub AddNewComputer (connection, netBiosName, smBiosGuid, macAddress)

        Dim inParams
        Dim outParams
        Dim siteClass
        Dim collection
        Dim collectionRule
       
        If (IsNull(smBiosGuid) = True) And (IsNull(macAddress) = True) Then
            WScript.Echo "smBiosGuid or macAddress must be defined"
            Exit Sub
        End If    

        If IsNull(macAddress) = False Then
            macAddress = Replace(macAddress,"-",":")
        End If   
       
        ' Obtain an InParameters object specific
        ' to the method.
       
        Set siteClass = connection.Get("SMS_Site")
        Set inParams = siteClass.Methods_("ImportMachineEntry"). _
            inParameters.SpawnInstance_()


        ' Add the input parameters.
        inParams.Properties_.Item("MACAddress") =  macAddress
        inParams.Properties_.Item("NetbiosName") =  netBiosName
        inParams.Properties_.Item("OverwriteExistingRecord") =  False
        inParams.Properties_.Item("SMBIOSGUID") =  smBiosGuid

        ' Add the computer.
        Set outParams = connection.ExecMethod("SMS_Site", "ImportMachineEntry", inParams)

      
       ' Add the computer to the all systems collection.
       set collection = connection.Get("SMS_Collection.CollectionID='SMS00001'")
      
       set collectionRule=connection.Get("SMS_CollectionRuleDirect").SpawnInstance_
      
       collectionRule.ResourceClassName="SMS_R_System"
       collectionRule.ResourceID= outParams.ResourceID
          
       collection.AddMembershipRule collectionRule

    End Sub

    I have tried to insert the snippet code (That imports the computer) into the connection code where specified but when I run it, I get nothing at all.

     

    Any chance anyone could provide

     

    a - Some guidence on the above

    b - links or information on some good material to learn this type of scripting

     

    Thanks in advance

Answers

  • Tuesday, June 10, 2008 12:10 PMJim Bradbury [MSFT]MSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    Well, I guess Chris has disappeared.

     

    I think folks here are happy to help, but need a starting point and, perhaps, a more specific question.

     

    Closing out old thread ...

     

    Jim Bradbury

All Replies

  • Thursday, June 05, 2008 3:19 PMJim Bradbury [MSFT]MSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hi,

     

    I was waiting for someone more experienced with OSD to chime in, but let me ask a couple of general questions, so I can point you in the right direction.

    • Have you done any scripting before? (it's o.k. to say no)
    • Have you used the SMS or ConfigMgr SDK before?
    • Are you able to run other simpler code examples from the SDK (like listing collections, programs or advertisements)?
    • Are you new to SMS/ConfigMgr?
    • Are you new to OSD?

    Sorry for the questions, I just want to respond with some practical information.

     

    Thanks,

     

    Jim Bradbury

     

     

     

     

     

     

     

  • Tuesday, June 10, 2008 12:10 PMJim Bradbury [MSFT]MSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    Well, I guess Chris has disappeared.

     

    I think folks here are happy to help, but need a starting point and, perhaps, a more specific question.

     

    Closing out old thread ...

     

    Jim Bradbury

  • Thursday, June 12, 2008 5:31 PMNathan Fisher Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    I see the thread was closed out, I was hoping someone can chime in here. 

     

    Here's my need - we would like to programmatically be able to add and delete members of a collection, whether it be the all systems or one we create.  However, we cannot write directly to the SQL.  I've been up and down the SDK and the internet and cannot seem to be able to find a way use VBSCRIPT to do this.  I have found things that seem close, like the above, but they just don't seem to work.  Here are specific examples:

    1) We'd like to have a non-SMS admin use a front end page to delete a single member of a collection at a time.  This will prevent a non-SMS admin from deleting the entire contents of a collection accidentally.  The user will populate a web page with a machine to remove, which will be written to a text file.  The name of the text file will be the PCname.txt, and I will parse out the machine name to get the PC variable to use in whatever script anyone can help with.  The vbscript will be running as a scheduled task to grab machines in the bin and run from a server to prevent a user from directly having access to the SCCM console.  I would like to hopefully use the WMI calls through SCCM, but I cannot seem to find anything....

     

     

     

     

    2) We also have a need to have non-admins to add a PC to a collection, such as authenticating a licensed app.  When it is authenticated internally, a person will go to a page, put in the machine name and collection ID (from a drop down box) and it will place a text file on the server, running another scheduled task to grab the text file (name) and the contents (collection) and add the PC to that collection. 

     

    One thing I did see was within the OSD section of the SDK, but it required a MAC address.  It seems vbscript does not support returning the MAC address (if you have some code for that let me know), but it seems powershell does.  I don't see anything in the SDK on powershell, so I didn't want multiple scripts moving aboout. 

     

    Why does the SDK not really deal with manipulating PCs within collections? 

     

    Thanks!

     

  • Friday, June 13, 2008 10:38 AMTorsten [MVP]MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

     Nathan Fisher wrote:
    Why does the SDK not really deal with manipulating PCs within collections?

     

    The SDK does cover that topic! For example: "How to Create a Dynamic Collection", "How to Create a Static Collection". It's just a matter of static (direct membership) or dynamic (query based) collections.

  • Tuesday, June 17, 2008 2:08 PMNathan Fisher Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    When you go there in the SDK, it tells you to create a collection - static or dynamic.  My interest is using the WMI provider to populate a machine name to a table.

     

    My other HUGE interest was deleting a PC from the all systems programmatically.  For instance, we have much more than 25,000 machines and deleting machines from this we'd like to do with a front end that can delete a machine rather than give others access to the admin console (see "delete special" and no more details are needed).  I have been able to do a query from SMS_R_SYSTEM to match a desired PC, but I'd like to take it a step further to delete the entire record when it finds that PC.  It seems there is virtually no code out there that connects to the WMI provider, matches a PC name from input, then deletes this PC record from SMS_R_System.

     

    Our other option was to try and change the agent time to 2001 and it would be removed the next day.  Problem is, I am able to connect to a record for a machine in SMS_R_SYSTEM, but when trying to do nested recursion to display the multiple times to manipulate, I receive "(null):0x80041017" or "(null):0x80041001" from my vbscript when even trying to view, forget about trying to re-write the record.  The idea was to pull out the string, take the "2008" part of agent time and replace it with "2001" and put it back in. 

     

    Either one of these two methods will work for deleting in our environment, but I am finding the SDK is a little light on this type of thing.  I can create collections, packages, advertisements, etc etc...but it is hard to remove a PC from the all systems collection and hard to put a machine into a direct membership.

     

    I think we are going to populate an external table with machine name and collection ID.  When we have our collection designed, it will query this external table for the matching collection ID and return a PC name, which will be matched with the name in SMS_R_SYSTEM for a JOIN in WQL.  Therefore, we can manipulate this table external from SCCM using vbscript and ASP while still populating a collection.  Sound reasonable?  We need to have people other than our team adding machines to collections that exist - someone 4000 miles away can purchase a Project 2007 license and go to a web page to insert someone into the collection, and the users within several hours will have it advertised to their machine.  Sound interesting?  We have issues in that we cannot directly write to the SCCM tables using SQL I'm told, so we have a need, and adding an external table we reference for queries sounds like a decent workaround.  Thoughts? 

  • Tuesday, June 17, 2008 2:54 PMTorsten [MVP]MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Static or dynamic membership rules are defining which computers will be members of a collection. So that's exactly what you were looking for. You do not have to modify WMI directly or SQL for doing so! Use SDK stuff and let the SCCM provider do this for you ... otherwise it's not supported.

     

    Here's an example for deleting a client record: http://blogs.msdn.com/rslaten/archive/2006/02/14/How-to-delete-an-SMS-client-throughout-your-hierarchy.aspx. It's for SMS 2003 but it should work with ConfigMgr, too.

  • Tuesday, June 17, 2008 4:09 PMJim Bradbury [MSFT]MSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Nice find Torsten! I'd forgotten about Russ' script and was just typing up an example of deleting a collection member.

     

    Chris,

     

    Thanks for clarifying your question ... it's now clearer why you posted the OSD code examples (which aren't quite what you're after).

     

    A couple of suggestions below: 

     

    1. Enumerating collection members - See How to Enumerate the Members of a Collection

    2. Adding computers - See How to Create a Static Collection (by adding membership rules)

    3a. Deleting computers (from Configuration Manager) - See Russ' script (find the object and call the delete method)

    3b. Deleting computers (from a static collection) - Enumerate through the membership rules and delete the appropriate membership rules.

    4. Have users add computers to a collection - (just some thoughts) ... either give folks rights to update your static collections or write a web service that your frontend talks to and allow the web service to do the work. We don't cover this in the SDK, but I know some customers have done work in this area. I've heard this commonly referred to as a "self-service portal", or generically as a "web console".

     

    You are correct in your earlier statement, we do not support direct updates to the database tables. Instead, as Torsten mentioned, use the SMS Provider for any updates.

     

    I hope that this helps!

     

    Jim Bradbury

     

     

     

  • Wednesday, June 18, 2008 2:15 PMNathan Fisher Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

     

    "4. Have users add computers to a collection - (just some thoughts) ... either give folks rights to update your static collections or write a web service that your frontend talks to and allow the web service to do the work. We don't cover this in the SDK, but I know some customers have done work in this area. I've heard this commonly referred to as a "self-service portal", or generically as a "web console". "

     

    I've got some great information about deleting an object from the all users collection above.  I am still working on the other project though, adding members to a collection.  We'd like to have admins (not SMS admins) from all over the globe be able to add PCs to a collection - not through the console.  Unfortunately, all we use is query-based collections, and with this, I have taken notice of a query for a particular piece of SW:

     

    select SMS_R_SYSTEM.ResourceID,SMS_R_SYSTEM.ResourceType,SMS_R_SYSTEM.Name,SMS_R_SYSTEM.SMSUniqueIdentifier,SMS_R_SYSTEM.ResourceDomainORWorkgroup,SMS_R_SYSTEM.Client from SMS_R_System where SMS_R_System.Name in ("PC1","PC2","PC3")

     

    These items are manually entered by an SMS admin when a machine is to be added.  I have been advised I have one of two courses of action, most likely:

     

    1) Programmatically GET this string somehow from WMI and append machine names to it using a web interface to get information into a text file then write a script to populate this.  I have no idea where this is in WMI

    2) Programmatically create a query for the collection each time an admin wants to add machines, and if the string is too long, it has to be in more than one query.  This will also be using the text file based concept to eliminate admin rights to the console.

     

    I'm pretty new to SCCM and what WMI can provide.  I have worked with vbscript for years with being pretty efficient at understanding what it can do and copying and pasting code in where needed. 

     

    What I'd like to know is the following:

    1) How can I programmatically add PCs to this query if we are not doing direct memberships?

    2) Is there a better way of doing this, something that is industry standard?  I have been told that we have issues with direct memberships due to duplicate GUIDs and the query based is much better.  If there's any suggestions on first...the concept of how to do this if possible, and two, maybe some links where these concepts are put into code. 

     

    I have a tough challenge here and not sure if this is possible.  I first started with the concept of an external database linking for the names, but have been advised it isn't good because of maintaining the external db link for child sites, etc.

     

    Any advice, or am I taking on something that really can't be done given limitations?

     

    Thanks!

     

     

  • Wednesday, June 18, 2008 4:20 PMJim Bradbury [MSFT]MSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

     

    Hi,

     

    That is a query ... though not a query that one would typically use to build 'dynamic' collections. There are some concerns around using direct membership collections, but that has mostly to do with having to manage them ... which is what you plan to do anyway. However, I've also seen some posts about performance in very large environments.

     

    In any case:

     

    The query is just a string, so you can certainly modify it using normal string manipulation.

     

    However, the collection rules, whether query-based or direct membership are stored as embedded objects. So, to list or manipulate the rules, you need to you'll need to open up SMS_Collection property / embedded object "CollectionRules", enumerate through the array and get the rule properties.

     

    Note: There are two different possible embedded objects types here, SMS_CollectionRuleQuery and SMS_CollectionRuleDirect, each with different properties. If you're enumerating a query-based collection, the properties in SMS_CollectionRuleQuery are available (including QueryExpression) and if you're enumerating a direct membership collection, the properties of SMS_CollectionRuleDirect are available.

     

    There are some good examples of listing and changing embedded object properties in the Maintenance Window section of the SDK.  See these topics:

    • How to List the Maintenance Windows and Properties for a Specific Collection
    • How to Change the Maintenance Window Properties for a Collection

    I did a quick test using C# based on the Maintenance Window examples, which is why I think they're a good starting point.

     

    Possible problem: I'm not sure what your requirements are, but if you're talking about adding a lot of systems by specifying system names in your query, then you may run into a problem with the maximum length of a query.

     

    I hope this helps!

     

    Jim Bradbury


    [edit]

     

    Adding some example C# code that accesses the collection query (the Maintenance Window examples provide more detail on adding and changing embedded properties):

     

    public void EnumerateCollectionRules(WqlConnectionManager connection, string collectionIDToEnumerate)
    {
        try
        {
            // Get a specific collection instance.
            IResultObject collectionToEnumerate = connection.GetInstance(@"SMS_Collection.CollectionID='" + collectionIDToEnumerate + "'");

     

            // Output the collection ID and name.
            Console.WriteLine("Collection:      " + collectionToEnumerate["CollectionID"].StringValue);
            Console.WriteLine("Collection name: " + collectionToEnumerate["Name"].StringValue);
            Console.WriteLine(" ");

     

            // Create a new array list to hold the collection rules objects.
            List<IResultObject> collectionRulesArray = new List<IResultObject>();
           
            // Populate the array list with the existing collection rules.
            collectionRulesArray = collectionToEnumerate.GetArrayItems("CollectionRules");

     

            // Enumerate through the array list to access each collection rule object and output specific properties for each object.
            foreach (IResultObject collectionRule in collectionRulesArray)
            {
                Console.WriteLine("Collection Rule Properties");
                Console.WriteLine("----------------------------- ");
                Console.WriteLine("Name:      " + collectionRule["RuleName"].StringValue);
                Console.WriteLine("Query:     " + collectionRule["QueryExpression"].StringValue);
                Console.WriteLine(" ");
            };
               
        }
        catch (SmsException ex)
        {
            Console.WriteLine();
            Console.WriteLine("Failed to enumerate collection rules / properties. Error: " + ex.Message);
        }
           
    }