Monday, July 28, 2014

PowerShell (Bangalore | Hyderabad ) UG - First Hangout

Recently we have started with the hangouts covering the basics of PowerShell.

The first hangout was aired successfully on 25th August 2014 and is now available on the "PowerShell Bangalore User Group" YouTube channel, 

We talked a little bit on the background of Windows PowerShell and how it makes an ITPro's life awesome and why is it very important to get started with PowerShell.


The video can be found below:




We learned a few lessons on the feedback from community on how to improve these in future.

So hopefully we will improve in future :)
 

Cheers !

Monday, July 14, 2014

PowerShell + SCCM 2012 : Get-DPContent (Get all the Packages stored on a DP)

This week when came back from office , saw this tweet from Adam Bertram:



It got me curious how to Script this, I had written a Script in past to remove packages from the DP. So let's try out this scenario.

The class to query is the SMS_DistributionPoint class and the property to be used for filtering is ServerNALPath. My Lab has only one DP named "DexSCCM" so gonna use that in the filter. Note the WQL operator used here for filtering is LIKE not '='.


001
Get-CimInstance -ClassName SMS_DistributionPoint -Filter "ServerNALPAth LIKE '%DexSCCM%'" -ComputerName dexsccm -Namespace Root\SMS\Site_DEX


This will give you a bunch of objects back , below is one of the objects screenshot:




Note - Please take a moment to go to the MSDN documentation of the SMS_DistributionPoint WMI Class and pay attention to the ObjectTypeID, SecureObjectID and PackageID properties.

From the above Objects I can get the PackageID or SecureObjectID and get an instance of the appropriate Object based on ObjectTypeID. :)

For the above Screenshot the ObjectTypeID is 2 which as per MSDN documentation says it corresponds to a SMS_Package class so let me get the instance of SMS_Package having the PackageID "DEX00001"



PS> Get-CimInstance -ClassName SMS_Package -Filter 'PackageID="DEX00001"' -ComputerName dexsccm -Namespace Root\SMS\Site
_DEX


ActionInProgress               : 0
AlternateContentProviders      :
Description                    : This package is created during installation.
ExtendedData                   :
ExtendedDataSize               : 0
ForcedDisconnectDelay          : 5
ForcedDisconnectEnabled        : False
ForcedDisconnectNumRetries     : 2
Icon                           :
IconSize                       : 0
IgnoreAddressSchedule          : False
ISVData                        :
ISVDataSize                    : 0
IsVersionCompatible            : True
Language                       :
LastRefreshTime                : 2/9/2014 2:23:00 PM
LocalizedCategoryInstanceNames : {}
Manufacturer                   : Microsoft Corporation
MIFFilename                    :
MIFName                        :
MIFPublisher                   :
MIFVersion                     :
Name                           : User State Migration Tool for Windows 8
NumOfPrograms                  : 0
PackageID                      : DEX00001
PackageSize                    : 48381
PackageType                    : 0
PkgFlags                       : 0
PkgSourceFlag                  : 2
PkgSourcePath                  : C:\Program Files (x86)\Windows Kits\8.1\Assessment and Deployment Kit\User State
                                 Migration Tool
PreferredAddressType           :
Priority                       : 2
RefreshPkgSourceFlag           : False
RefreshSchedule                :
SecuredScopeNames              : {Default}
SedoObjectVersion              : FF4B4436-5124-4875-ADB3-168BF444068C
ShareName                      :
ShareType                      : 1
SourceDate                     : 2/9/2014 2:06:15 PM
SourceSite                     : DEX
SourceVersion                  : 1
StoredPkgPath                  :
StoredPkgVersion               : 1
Version                        : 6.3.9431.0
DefaultImageFlags              : 2
IsPredefinedPackage            : False
TransformAnalysisDate          : 1/1/1980 5:30:00 AM
TransformReadiness             : 0
PSComputerName                 : dexsccm


It works ! Got the Object back and following this approach can get the Objects stored on the DP.

But there is a small catch when the ObjectTypeID is 31 (SMS_Application) then we can't use the PackageID as a filter with the SMS_Application Class because it is a lazy property. If you don't believe me go ahead and try it (already did). So we have to use another property ModelName to filter.

To assist on this wrote a small Function called Get-DPContent (download it) which allows you to specify a DPName and the ObjectType (application, package, image etc) you want report of being stored in a DP. One can modify the Function to include all details for the Objects but I was just looking for the names of the packages, applications etc. Below is how you use it (once you have it dot sourced)


PS C:\> get-DPContent -DPname dexsccm  

DP                                        ObjectType                               Package                                  PackageID                               
--                                        ----------                               -------                                  ---------                               
dexsccm                                   Package                                  User State Migration Tool for Windows 8  DEX00001                                
dexsccm                                   Package                                  Configuration Manager Client Package     DEX00002                                


By default it only looks for Packages
One has to specify the ObjectType in order to look for Applications :


PS> Get-DPContent -DPname dexsccm -ObjectType Application -SCCMServer dexsccm

DP                            ObjectType                    Application                   Description
--                            ----------                    -----------                   -----------
dexsccm                       Application                   7-Zip                         For 64-bit machines only
dexsccm                       Application                   PowerShell Community Exten... PSCX 3.1
dexsccm                       Application                   Quest Active Roles Managme... Quest Snapin for AD Admins...
dexsccm                       Application                   PowerShell Set PageFile       The Script will set the  P...
dexsccm                       Application                   NotePad++                     Alternative to Notepad


Hope this helps someone, trying to generate a list of Packages on a DP :)
This can be modified to work with SCCM 2007 too but seriously are you still running that :P
Cheers !

Resources:

Technet Gallery: Get list of Packages stored on a DP in ConfigMgr/ SCCM 2012
http://gallery.technet.microsoft.com/Get-list-of-Packages-35208e83/



SMS_Distribution WMI Class
http://msdn.microsoft.com/en-us/library/hh949735.aspx




Monday, July 07, 2014

PowerShell + SCCM 2012 : Boundaries

This is the second post in continuation of Getting Started series (not diving into the WMI as of now), hoping to cover few of the common tasks which ConfigMgr admins perform in the series.


[UPDATE] - Thanks to TechSmith  for giving me a free MVP copy of the Camtasia & SnagIt. Now will try to put links to youtube videos instead of the animated GIF's.

So first of the simple tasks which you can do with CM cmdlets is on the topic managing boundaries. Not gonna show each and every cmdlet but few use cases.

Note - Assuming you have the PowerShell console loaded with the CM Module (see the Getting Started post for this)

First let's ask PowerShell which cmdlets have *boundary* pattern in the noun :


PS> Get-Command -Module configurationmanager -Noun *boundary*

CommandType     Name                                               ModuleName
-----------     ----                                               ----------
Cmdlet          Add-CMBoundaryToGroup                              configurationmanager
Cmdlet          Get-CMBoundary                                     configurationmanager
Cmdlet          Get-CMBoundaryGroup                                configurationmanager
Cmdlet          New-CMBoundary                                     configurationmanager
Cmdlet          New-CMBoundaryGroup                                configurationmanager
Cmdlet          Remove-CMBoundary                                  configurationmanager
Cmdlet          Remove-CMBoundaryFromGroup                         configurationmanager
Cmdlet          Remove-CMBoundaryGroup                             configurationmanager
Cmdlet          Set-CMBoundary                                     configurationmanager
Cmdlet          Set-CMBoundaryGroup                                configurationmanager

One of the best thing I keep telling is the Verb-Noun naming convention for the cmdlets and the help subsystem making them so easy to discover.

Note- Please update your help using either Update-Help (internet access needed) or Save-Help to get most out of it or use the -online switch with get-help to view the latest help in your default browser.



Create New Boundaries in Bulk


Take a wild guess on the cmdlet which will do this :D

New-CMBoundary can be used to create all 4 type of boundaries in ConfigMgr

 
One can create a new IPSubnet boundary by using below example..Make a note of how you specify the value of IPAddress & Mask which is used to calculate the NetworkAddress (SubnetID):


001
New-CMBoundary -Name Boundaryname -Type IPSubnet -Value '10.1.1.0/16' -Verbose
Look at the help for the cmdlet to get more examples.

A fairly simple usage for this cmdlet can be that you have a CSV file having the information on the boundaries that are to be created and you can use the Import-CSV to get the Objects and pipe it to New-CMBoundary cmdlet.



The above demo is a very crude example but should give you an idea on what PowerShell can do for you :)

Use Case : Get IPRange Boundaries an IPAddress belongs too


Another Cool thing which can be done using PowerShell is to look for which boundary (only IP Range boundaries) an IP Address falls in. This is very helpful in troubleshooting. Have used a Function named Get-IPAddressBoundary below which is based on Nickolaj's Post (have just modified it to return Objects back as per my requirement)

You can find my modified Function at Technet Gallery here

Below is how one uses it (you have to dot source the function first):

PS> Get-IPAddressBoundary -SiteServer dexsccm -IPAddress 10.1.1.110


IPAddress     : 10.1.1.110
BoundaryFlags : 0
BoundaryID    : 16778243
BoundaryType  : 3
DisplayName   : testBoundary2
CreatedBy     : DEXTER\Administrator
ModifiedBy    : DEXTER\Administrator
Date Created  : 6/24/2014 7:14:05 PM
Date Modified : 6/24/2014 7:14:05 PM

If you have an IP Address which falls under more than one IPRange Boundary, then it returns multiple objects. I created deliberately one more Boundary to show this example( later removed it).


PS> Get-IPAddressBoundary -SiteServer dexsccm -IPAddress 10.1.1.99


IPAddress     : 10.1.1.99
BoundaryFlags : 0
BoundaryID    : 16778242
BoundaryType  : 3
DisplayName   : testBoundary1
CreatedBy     : DEXTER\Administrator
ModifiedBy    : DEXTER\SMSadmin
Date Created  : 6/24/2014 7:14:05 PM
Date Modified : 6/25/2014 3:48:15 AM

IPAddress     : 10.1.1.99
BoundaryFlags : 0
BoundaryID    : 16778245
BoundaryType  : 3
DisplayName   : Overlapping Boundary
CreatedBy     : DEXTER\SMSadmin
ModifiedBy    : DEXTER\SMSadmin
Date Created  : 6/26/2014 5:18:25 PM
Date Modified : 6/26/2014 5:18:25 PM


Another User Case : Process IPSubnet Boundaries to get more information

One more use case (Thanks to @Nickolaj for suggesting this to me) is to get the NetworkAddress (Subnet ID) , BroadCastAddress & IPRange for a IPSubnet boundary. 

But there is a catch to this..you should have created your IPSubnet boundary using PowerShell for this to work...e.g "TestBoundary3" created in the animated GIF   video above

You must be wondering, Why this works only with boundaries created through PowerShell ? 
If you create an IPSubnet boundary using the Console it doesn't store the mask information anywhere..it does take the IP Address & Subnet mask as input but as soon as you. Click "Apply" a network address(SubnetID) is calculated and rest is removed the next time you open the properties.

See the video below showing that , earlier had animated GIF but my friend and a long time ConfigMgr community member @Harjit suggested to go with short video clips :)





Let's compare the Boundaries of BoundaryType = 0 (IPSubnet type) created from a CSV and the one created with the ConfigMgr Console. Take a note of the "Value" property.


PS> Get-CMBoundary | where BoundaryType -eq 0 | Format-Table -Property DisplayName,BoundaryType,Value -AutoSize

DisplayName   BoundaryType Value
-----------   ------------ -----
TestBoundary3            0 10.1.1.0/16
TESTIPSUBNET             0 10.2.0.0


Note - The Function (used later) uses the property "Value" to do its magic as the Function requires to be passed a Subnet Mask to calculate extra bits of information.

Bottom line , we need to have SubnetMask store in the Value property of the Boundary Object. See the result when we pipe the above 2 boundaries to the Function

Find the function --> Convert-IPSubnetToIPRangeBoundary 
Change the Name if you don't like it.

Here is how you use it:


PS DEX:\> Get-CMBoundary | Convert-IPSUbnetToIPRangeBoundary 


NetworkAddress   : 10.1.0.0
BroadCastAddress : 10.1.255.255
HostMin          : 10.1.0.1
HostMax          : 10.1.255.254
IPRange          : 10.1.0.1  - 10.1.255.254
BoundaryFlags    : 0
BoundaryID       : 16778244
BoundaryType     : 0
CreatedBy        : DEXTER\Administrator
CreatedOn        : 6/24/2014 7:14:06 PM
DefaultSiteCode  : 
DisplayName      : TestBoundary3
GroupCount       : 0
ModifiedBy       : DEXTER\Administrator
ModifiedOn       : 7/5/2014 5:08:41 PM
SiteSystems      : 
Value            : 10.1.1.0/16

NetworkAddress   : 0.0.0.0
BroadCastAddress : 255.255.255.255
HostMin          : 0.0.0.1
HostMax          : 255.255.255.254
IPRange          : 0.0.0.1  - 255.255.255.254
BoundaryFlags    : 0
BoundaryID       : 16779246
BoundaryType     : 0
CreatedBy        : DEXTER\SMSadmin
CreatedOn        : 7/6/2014 4:48:45 AM
DefaultSiteCode  : 
DisplayName      : TESTIPSUBNET
GroupCount       : 0
ModifiedBy       : DEXTER\SMSadmin
ModifiedOn       : 7/6/2014 4:48:45 AM
SiteSystems      : 
Value            : 10.2.0.0

For the Boundary name "TESTIPSUBNET" the first 5 note properties added are not valid (NetworkAddress -to- IPRange).

So what I am gonna do is update it with the mask value using the cmdlet Set-CMBoundary now:


001
Set-CMBoundary -Name TESTIPSUBNET -Value "10.2.2.0/16" -Verbose

Now the Mask value i.e. 16 is added to the Value property of the Boundary Object. Now let's run the function again.



PS DEX:\> Get-CMBoundary | Convert-IPSUbnetToIPRangeBoundary 


NetworkAddress   : 10.1.0.0
BroadCastAddress : 10.1.255.255
HostMin          : 10.1.0.1
HostMax          : 10.1.255.254
IPRange          : 10.1.0.1  - 10.1.255.254
BoundaryFlags    : 0
BoundaryID       : 16778244
BoundaryType     : 0
CreatedBy        : DEXTER\Administrator
CreatedOn        : 6/24/2014 7:14:06 PM
DefaultSiteCode  : 
DisplayName      : TestBoundary3
GroupCount       : 0
ModifiedBy       : DEXTER\Administrator
ModifiedOn       : 7/5/2014 5:08:41 PM
SiteSystems      : 
Value            : 10.1.1.0/16

NetworkAddress   : 10.2.0.0
BroadCastAddress : 10.2.255.255
HostMin          : 10.2.0.1
HostMax          : 10.2.255.254
IPRange          : 10.2.0.1  - 10.2.255.254
BoundaryFlags    : 0
BoundaryID       : 16779246
BoundaryType     : 0
CreatedBy        : DEXTER\SMSadmin
CreatedOn        : 7/6/2014 4:48:45 AM
DefaultSiteCode  : 
DisplayName      : TESTIPSUBNET
GroupCount       : 0
ModifiedBy       : DEXTER\Administrator
ModifiedOn       : 7/7/2014 7:45:21 PM
SiteSystems      : 
Value            : 10.2.2.0/16

I think showed you the difference and how to use the function. Let me know if this is helpful to you and if any suggestions on how I can improve it :)

One can use the Get-IPAddressBoundary and tweak it a bit to accept the pipeline input of the above Convert-IPSubnetToIPRangeBoundary...It's pretty straighforward, but wanna leave that to your imagination :P

Rest of the Boundary & Boundary Groups management stuff is easy to figure out using the Get-Help and Get-Command. Explore and Enjoy !


Resources


Check if an IP Address is within an IP Range boundary in ConfigMgr 12  - Nickolaj Andersen
http://www.scconfigmgr.com/2014/04/15/check-if-an-ip-address-is-within-an-ip-range-boundary-in-configmgr-2012/

Technet : Modified Get-IPAddressBoundary Function
http://gallery.technet.microsoft.com/Get-Boundaries-an-5fa3a1c4

Technet : Convert-IPSUbnetToIPRangeBoundary Function
http://gallery.technet.microsoft.com/Convert-IPSubnet-SCCM-a03d1cac


Thursday, July 03, 2014

PowerShell + WMI - Static & Instance Methods

One has to get their feet wet with WMI when it comes to managing ConfigMgr with PowerShell. How ConfigMgr uses WMI ? Not only ConfigMgr but for other products if you leverage WMI with PowerShell then understanding what are Static & Instance methods is important.

But there are lot of places where People stumble ( I did too ). Below is a question asked in Hyderabad PowerShell User Group (PSHUG) showing one of the common point of confusion.



If you are a ConfigMgr Admin then you already know that in SCCM client we have a lot many Client actions that can be triggered remotely. Below are the available SCCM Client actions (In CM 2012) :





Going back to the question, below code will work,which is triggering "Software Inventory" on a remote $machine:


001
002
$Client1 = $([WmiClass]"\\$Machine\ROOT\ccm:SMS_Client")
$Client1.TriggerSchedule("{00000000-0000-0000-0000-000000000021}")

One can get more info on the class SMS_Client & TriggerSchedule Method.

But why are we not able to see the method when we do the below:


001
002
$Client2 = Get-WmiObject -ComputerName $Machine -Namespace root\ccm -Class SMS_Client
$Client2 | Get-Member -MemberType *

Note - There are two variables $Client1 & $Client2 


Before we go further would like to clarify one thing first, one can get the WMI Class Object by using the Get-WMIObject cmdlet itself with -List switch

So If I do the below..take note of the -List switch at the end:

001
$Client3 = Get-WmiObject -Class SMS_client -Namespace Root\CCM -ComputerName $machine -List


Go ahead and see for yourself that the two objects $Client1 and $Client3 are essentially the same :) 

The answer to why the TriggerSchedule method doesn't appear when we pipe it to Get-Member is really simple, Just compare the Type of both the Objects you have at hand



So from above we deduce that those two are essentially 2 different objects so you won't find the TriggerSchedule Method on the $Client2 ;)

This becomes obvious if we think over it --> [WmiClass] type accelerator gives you the WMI Class Object back not the WMI Instance


Detailed Explanation


To clarify things a bit more the Class ($Client1) is a definition of Objects and Instance (Client2) is the manifestation of the class. 

In our question $Client1 is Class and $Client2 is an Instance of the class.

Now a class can have 2 type of methods : 

  1. Static Methods (only available on the class)
  2. Instance Methods (available on the Instances of the class)

So how do we go on getting this key piece of information, whether it is a Static method or not ?

Once you have the class object ($Client1 ) you can explore it. 

So once we have the WMI Class Object we can do the below to take a look at the methods and the qualifiers:
PS> $Client1 | select -ExpandProperty methods | select name,qualifiers

Name                                                        Qualifiers
----                                                        ----------
ResetPolicy                                                 {implemented, static}
RequestMachinePolicy                                        {implemented, static}
EvaluateMachinePolicy                                       {implemented, static}
TriggerSchedule                                             {implemented, static}
RepairClient                                                {implemented, static}
SetAssignedSite                                             {implemented, static}
GetAssignedSite                                             {implemented, static}
SetGlobalLoggingConfiguration                               {implemented, static}
ResetGlobalLoggingConfiguration                             {implemented, static}
SetClientProvisioningMode                                   {implemented, static}

Notice that all the methods on the class are static.




To sum it all let's take a class having both Static and Instance method e.g Win32_Share

PS> Get-WmiObject -Class WIn32_Share -List| select  -ExpandProperty methods | select name,qualifiers

Name                                                        Qualifiers
----                                                        ----------
Create                                                      {Constructor, Implemented, MappingStrings, Static}
SetShareInfo                                                {Implemented, MappingStrings}
GetAccessMask                                               {Implemented, MappingStrings}
Delete                                                      {Destructor, Implemented, MappingStrings}


So the only method which is static is the Create Method and rest are Static.

Verify this is correct by getting the WMI Instances back and using the Get-Member


PS> Get-WmiObject -Class WIn32_Share  | Get-Member -MemberType method -Force


   TypeName: System.Management.ManagementObject#root\cimv2\Win32_Share

Name          MemberType Definition
----          ---------- ----------
Delete        Method     System.Management.ManagementBaseObject Delete()
GetAccessMask Method     System.Management.ManagementBaseObject GetAccessMask()
SetShareInfo  Method     System.Management.ManagementBaseObject SetShareInfo(System.UInt32 MaximumAllowed, System.St...


Notice that the Create method doesn't show up on the WMI Instance cause it is a static method on the class :)

But it does show up on the WMI Class Object


PS> Get-WmiObject -Class win32_Share -ComputerName $Machine -List | Get-Member -MemberType Method


   TypeName: System.Management.ManagementClass#ROOT\cimv2\Win32_Share

Name   MemberType Definition
----   ---------- ----------
Create Method     System.Management.ManagementBaseObject Create(System.String Path, System.String Name, System.UInt3...


I will be presenting a session on the next PSBUG + BITPro meet on the topic
"PowerShell + ConfigMgr - Getting Started" on 19th July, in this session will shed light on this too. The registrations haven't opened yet. So you can keep an eye on the FB group links.

Tuesday, July 01, 2014

PowerShell + SCCM 2012 : Add setting to a CI using PowerShell

Working with Peter van der Woude on how to add settings to a Configuration Items using PowerShell. I went with the manipulating the SDMPackageXML approach and Peter is working on a WQL Query way of doing this (can't wait to see his code).

Hoping that you know how to create a Configuration Item (CI) in ConfigMgr and how to add settings to a CI. This post is only on how to add a folder setting to a CI. Based on this methodology will try to add few more settings in the upcoming posts.

Let's get the ball rolling :

First get the prerequisites out of the way


001
002
003
004
005
006
007
008
009
010
011
012

#Load the ConfigurationManager Module
Import-Module -Name "$(split-path $Env:SMS_ADMIN_UI_PATH)\ConfigurationManager.psd1"


#Change location to my CMSite Provider
Cd DEX:


#Add the Assembly Reference to the DCM DLL
Add-Type -Path "$(Split-Path $env:SMS_ADMIN_UI_PATH)\Microsoft.ConfigurationManagement.DesiredConfiguration.dll" -Verbose

To begin with let's create a Configuration Item using the Cmdlet New-CMConfigurationItem and then load the xml into a variable $oldxml :

001
002
003
004
005
006
#create the CI and store it in a variable
$testCI = New-CMConfigurationItem -CreationType WindowsOS -Name "DexterPStest" -Description "Testing PS automation" -Verbose

#load the XML
$oldxml  = New-Object -TypeName xml
$oldxml.LoadXml($($testCI.SDMPackageXML))

After the above you should see a CI in the console (Note it's empty, no settings and the Revision is 1):






Now let's create a test Folder Setting


001
002
003
004
005
006

#now create a Folder setting
$foldersetting = New-Object -TypeName Microsoft.ConfigurationManagement.DesiredConfiguration.Settings.FolderSetting -ArgumentList "File_$([guid]::NewGuid())",'test','PS Automation'
$foldersetting.path = "C:\temp\test"
$foldersetting.FolderName = "Dexter"

The Settings you create have a method named SerializetoXML on them which can be used to get the XML, it needs an XMLWriter Object to be passed to it. So we create an XMLWriter Object with required settings and write the XML to a file (as I wanted to see the XML) but this can be done entirely using streams too:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018

#create the XML Writer Settings
$settings = New-Object system.Xml.XmlWriterSettings 
$settings.Indent = $true 
$settings.OmitXmlDeclaration = $false 
$settings.NewLineOnAttributes = $true 

# Create a new Writer
$writer = [system.xml.XmlWriter]::Create("C:\Temp\FolderSetting.xml", $settings) 

#Now let's serialize the XML and let it be written to our XML Writer
$foldersetting.SerializeToXml($writer)

#Flush the contents to the file
$writer.Flush()

#disposr the Writer Object
$writer.Dispose()

You would get a similar XML file :

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017

 <?xml version="1.0" encoding="utf-8"?>
<Folder
  LogicalName="File_49225e34-6398-4f90-8a93-17a203195784"
  Is64Bit="false"
  Depth="Base" xmlns="http://schemas.microsoft.com/SystemsCenterConfigurationManager/2009/07/10/DesiredConfiguration">
  <Annotation xmlns="http://schemas.microsoft.com/SystemsCenterConfigurationManager/2009/06/14/Rules">
    <DisplayName
      Text="test"
      ResourceId="ID-b36d34e8-2ac1-4a02-90bb-be17cd5e8b10" />
    <Description
      Text="PS Automation"
      ResourceId="ID-8f57bc35-a94b-4a6c-9e50-ddebcb549e60" />
  </Annotation>
  <Path>C:\temp\test</Path>
  <Filter>Dexter</Filter>
</Folder>


As I wrote the XML to a file I have to load it back. After that I need to Import the new FolderXML using the ImportNode method on the $OldXML object. Once it is imported you can append the child. But how do we know the node where we append the imported node ?

You can tackle this in 2 ways:

  1. Create a sample CI and study the SDMPackageXML property on it.
  2. If you have ConfigMgr SDK then go to the Samples>DesiredConfigurationManagement>Schema , there is a doc which shows how to author the XML digest using Visual Studio.
I was feeling a bit lazy so I went with the first approach ;)




001
002
003
004
005
006
007
008
009
010
011
012
013
#let's load the XML now
$FolderXML = New-Object -TypeName xml
$FolderXML.Load("C:\Temp\FolderSetting.xml")

#import the new Folder XML node to the old xml
$import = $oldxml.ImportNode($FolderXML.folder,$true)

#now add the above imported XML node
$oldxml.DesiredConfigurationDigest.OperatingSystem.Parts.AppendChild($import)

#save the new xml
$oldxml.Save("C:\temp\newxml.xml")

Now we have our XML ready, time to push this up to the CI instance.

We have a cmdlet by the name Set-CMConfigurationItem which allows us to specify the XML digest file to the parameter -DesiredConfigurationDigestPath. Let's give it a shot.

Note - I tried using the Put() method on the WMI instance of the CI it returns no error but it seems to do nothing. I will have to check on this later.


001
Set-CMConfigurationItem -DesiredConfigurationDigestPath C:\temp\newxml.xml -Name $testCI.Localizeddisplayname -Verbose

Oh Yeah ! it does work and I see the changes reflect in the CI settings tab:




I tried to keep things simple for now as there are lots of moving pieces and I want to tackle them one at a time.


001
Write-Output -InputObject "Cya till next post"

PowerShell MVP Award !

Around evening got a call from Ravikanth Sir, Congratulating me on the prestigious PowerShell MVP award. For a moment I couldn't believe that.

Things went on flashback.
When I first started my IT career and saw myself wasting a lot of time on the repetitive tasks.

That was the start of my PowerShell journey and honestly I haven't looked back as I knew what it would bring to the table, More laziness for me ;)

I consider myself lucky as from the beginning of my Career I had PowerShell to make my life easy and now I feel honored to be part of the elites.

Looking forward to sharing and spreading #PowerShell awesomeness.

This is dedicated to all the cool people I have met along the journey :)