Wednesday, December 25, 2013

SCCM 2007 + PowerShell - WMI is the key to automation Part 2

In Previous post I talked about how to explore the WMI namespace for ConfigMgr 07.
In this post would like to point out on how to use the SCCM-Commands PowerShell Module , this Module leverages WMI with PowerShell.

Many thanks to Michael Niehaus, Rikard Ronnkvist, Stefan Ringler and Ricardo Cheing authors of this Module for sharing this.

Download this module and extract it, it contains a single PowerShell Module file .this is a Script Module (SCCM-Commands.psm1). Place this File under your Modules Directory in a folder named same as the module i.e. SCCM-Commands



Now you should be able to see the Script module in you session like below:


Now you have to import the module and then you can list all the Advanced functions shipped with this module using either of the below 2 ways:
 1. Use Get-Command -Module SCCM-Commands
 2. Or Use Get-SCCMCommands function within the module itself.

Now the Functions in the Module are Advanced ones and don't have much documentation on how to use the module......though it is very intuitive.
So first you connect to the ConfigMgr 07 site Server having SMS Provider installed using Function Connect-SCCMServer (Confused on which hostname to supply refer my previous post to get the site System having SMS Provider installed on it).

Store the object returned back by the function for later use in the variable $SCCMServer :


Now the main task at my hand was to automate the Query Membership Rule Creation for a collection. So am going to head in that direction.


Querying Collection

First to get the details of a collection....use Get-SCCMCollection function. Now take a look at the syntax for the function below:

There is a mandatory parameter named -SccmServer which expects an Object as the input, we will pass the Object stored in $SCCMServer as an argument to it . There is an optional parameter by the name -Filter which takes filter in WMI Format.

There is a test collection in my TEST environment named "Deepak Test 1" having CollectionID 'SCM013EA' , Now I can use either the name or collectionid as the filter here.


Using the Name as the filter:

PS C:\> Get-SCCMCollection -SccmServer $SCCMServer -Filter "Name='Deepak Test 1'"


Similarly, we can use the CollectionID as the filter too. Below is how you will do it:

PS C:\> Get-SCCMCollection -SccmServer $SCCMServer -Filter "CollectionID='SCM013EA'" -Verbose

Now pipe the output of the above to Get-Member to see some interesting methods (marked ones) and properties on the Object. 

Now if you see some of these methods are quite interesting (marked above) which will ultimately help in automation of Query Based Deployments.... Keep hanging !



Querying Collection Rules

In the above  2 screenshots there is a property by the name CollectionRules but it is empty. So in order to query the present Collection Rules (Query or Direct) in a Collection we will use another function Get-SCCMCollectionRules . Before that we will store the Collection Object returned above in a variable to later make it easy to reference the properties.


For my test collection I have 3 Query Membership Rules already present...below is a screenshot of it:

collection When I seek the same info from PowerShell it does return be back the Query Rules:


If we select just the RuleName and QueryID then it show there are three Query Rules...which is the correct information:



This is it for this blog.
Well you can do add new Query Rules to a collection using the  Add-SCCMCollectionRule . There is not direct way to modify a Query Expression so we have to delete the Query Rule first modify it and Add it which will be the topic for my next post....




Saturday, October 19, 2013

SCCM 2007 + PowerShell - WMI is the key to automation Part 1

Well in the quest of automating ConfigMgr 07.....WMI is the key. Now this post isn't a primer on how to use WMI with PowerShell.
There are very good resources which you can hit like:
  1. Free Ebook by Ravi Sir (PowerShell MVP) - "WMI Query Language via PowerShell" (referred to me by @LaerteSQLDBA :) )
  2. Book by Richard Siddaway (PowerShell MVP) - "PowerShell and WMI"
There are other good resources like HeyScriptingGuy's blog where one can serach for WMI tag, there is whole bunch of information out there on leveraging WMI with PowerShell. If you haven't checked out this yet then you are missing something very cool to manage your Infrastructure.

That being mentioned the journey to exploring WMI starts by connecting to the SCCM Site System which has the SMS provider installed.
Open the ConfigMgr console > Connect to your Central Site/Primary Site > Site Management > Right -click site ...select Properties. The information where SMS provider ins installed can be fetched by site properties general tab in ConfigMgr MMC


After getting the Server having SMS provider installed, we are going to explore the SMS provider a bit for the relevant classes.
To explore WMI you can use many tools like :
  1. WMI CIM Studio (par of WMI Administrative tools from Microsoft)
  2. PowerShell based WMI Explorer created by Marc van Orsouw (aka /\/\O\/\/) hosted at powershell.org
  3. Sapien WMI Explorer (this needs you to register an account before downloading)
I am going to use WMI CIM Studio for this post on showing how to connect to SMS provider and the find the relevant classes for the Application deployment.
So fire on WMI CIM studio from  Start Menu > All Progams > WMI Tools > WMI CIM Studio

This will essentially open up an IE page...click on "Allow Blocked Content" when it prompts.

Later a pop-up asking which namespace to connect to will be presented. By default it will try to connect to your local machine's Root\CIMv2 namespace.
Click on the below highlighted icon :


After this it will ask you to browse the namespace. In the machine name field fill in the Server having SMS provider installed and in the starting namespace fill in "root\sms\site_<3 letter SiteCode>". Click Connect.




WMI CIM Studio will then prompt you for the login. Check the highlighted box if you want to connect as the current user or supply alternate credentials which have access on ConfigMgr 07 to perform tasks.



After this press "Ok" and wait until WMI CIM studio retrieves the classes definitions from the namespace.
Once done it will loaded with whole lot of SMS WMI classes..... Click on a Class to see the member definitions (Properties, Methods and Associations)



Now as part of the automation quest ....I was set on finding to automate the deployment tasks we do manually now explained here in this post.
In nutshell as part of our deployment activity we manipulate collection by creating Query Membership CollectionRule to which we add a custom query having different machine names , machines which we want to see reflected in the application collection so that the related application advertisements show up on that machine for the end user to install...Pheww !

That being said.....If one doesn't have any idea about which classes to use...then just start with hit and trial approach.
Now I am going to search for classes with keyword collection in it...

Click on the little binocular  icon on the WMI CIM studio.
Then enter "collection" keyword and hit Go button...then it will list all the classes containing the keyword "Collection" in it...Double-click the class to go to its definition. In this case am double-clicking on the class SMS_Collection .




After this we need to explore the SMS_Collection Class definition for the required property or method which will help us in creating QueryRules.
Go to the methods tab for this class.......



If you want to explore the method, just right-click and select Method Qualifiers



Check the Description what it says:
"This method is used to add new rules to the SMS_CollectionRule property. Returns the query ID if the rule was a query rule, 0 otherwise."  <-- Voila

We can later explore  the "Edit Method Parameters" (in above screenshot ) on how to invoke this method.



So I began making a list of interesting classes which help me in achieving the automation task I intended to do.
Few of these which you can explore are :
SMS_Collection
SMS_CollectionRule
SMS_CollectionRuleDirect
SMS_CollectionRuleQuery

Well the possibility are endless....if you are trying to manage distribution points then start exploring what WMI has to offer for them.
Similarly, go explore what you can do with packages, advertisements, programs etc.


More on this later.....Well we can always use PowerShell to query the WMI classes and check the members.

But I found this approach to do research better.
Next post I will explore on using already written PowerShell module for SCCM.


Monday, October 14, 2013

SCCM 2007 + PowerShell Automation - The Problem with Query Based Deployments


Few months back, I made a resolution of automating the deployment process used in our Project,
Scenario
So if a User needs applications in their machine , they raise a request for the same. After the approval process it comes to our team and we handle the deployment of these apps to the User machines using SCCM 2007 (or ConfigMgr).

So this is how we do it ....we have the QueryRules for the Application collection in place we just go there and add the machine name there manually in the Query Rule for the Application Collection. 

To go with the post I have a test collection in place by the name "Deepak Test 1"...So first I will be showing how we do this manually and then later on how I did it using PowerShell. There something called "Click Count" which is a rough count of how many mouse clicks am using to do these actions in ConfigMgr MMC Console (rough estimate)
Click Count = 1

Manual Way
Step 1. Right  Click and open the properties for the collection. On the "Membership Rules" tab, we have a QueryRule named "test query" for this demo.
Click Count = 2
Step 2. Double-Click the  QueryRule and then "Edit Query Statement"
Click Count = 3
Step 3. Now on the "Query Rule Properties" , you can add a machine in 2 ways either by clicking on the "Show Query Language" and then adding the machine name to QueryExpression (this one is not preferred) or click on the "Criteria" tab (which am gonna do )
Click Count = 4
Step 4: On the next prompt double-click the Query or click on the icon highlighted button to edit the query.
Click Count = 5
Step 5 : Now on the next window put the new machine names under Value to Add and click "Add" or click on any machine names under "Value to Match" to remove (in case application needs to be removed from the machine)
Click Count = 6
And then add other 4-clicks to click on "Ok" buttons 
So total Click Count = 10 (rough count) ...after adding the machine name we have to update the collection (1 more click) and then hit F5 to refresh the view.
See to do a normal task of adding/removing a machine name we had to perform 10-clicks or more..Now on a regular day basis we get like hundreds of these tickets (coz they started migrating VDIs to Win 7...Yep now they are doing it finally).

P.S. - WMI is the answer to managing/automating the ConfigMgr 07 deployments.

Monday, July 29, 2013

PowerShell to Create a random Music Playlist in VLC

This is a trick which got famous at the PowerShell Bangalore User Group (PSBUG) session.

Little trick to play a single random song provided VLC (or any other player) is set as default player for the .mp3 files:

  Posh (0002) >  Get-ChildItem -Path 'F:\songs\movies neew' -Include *.mp3 -Recurse | Get-Random -Count 1 | Invoke-Item 

But this won't create a playlist for you if you simply increased the count for Get-Random..try this
  Posh (0003) >  Get-ChildItem -Path 'F:\songs\movies neew' -Include *.mp3 -Recurse | Get-Random -Count 2 | Invoke-Item


Let's try creating a playlist this time ( have to set VLC as  the default player for .mp3 files , thus creating filetype associations for .mp3 files) 

You need get all the possible list values for mp3 extension.I read this excellent article by David Moravec at PowerShellMagazine to list all possible verb values for a particular extension.
After you go through the above mentioned article, you just use the below code to get a list of all extensions and the list of verbs associated with them

001
002
003
004
005
cmd /c assoc |
ForEach
{
    $ext = ($_ -split '=' )[0 ]"{0}: {1}" -f $ext, (( New-Object System.Diagnostics.ProcessStartInfo -ArgumentList "test$ext" ).Verbs -join ', ' )
}| Out-GridView -Title 'Verb values for associated extensions'

Now on the Out-Gridview window filter out using ".mp3" and see the list of verbs that we have



Now we have all the ingredients ready, First we get a list of all .mp3 files in a directory then randomly get 5 files out of it and at last foreach of those 5 files we start a process with the -verb parameter taking an argument of 'AddtoPlaylistVLC'

001
002
003
004
005
006

 get-childitem -Path 'F:\songs\movies neew' -Filter *.mp3 -Recurse |
    Get-Random -Count 5 |
    Foreach { Start-Process -FilePath $_.Fullname -verb 'AddtoPlaylistVLC' }


Wait and watch :)

Monday, July 15, 2013

using throw inside param() block

Recently I had to write  a very restrictive script which takes few arguments like a filename, computername etc.
These parameters need to be compulsory otherwise the script should not run...

Below is the first function I came up with(nothing fancy):

function Test-AdvancedFunction {
     [CmdletBinding()]
      param(
           [Parameter(Position =0, Mandatory =$true )]
           [ValidateScript({Test-path $_})]
           [ System.String]
            $FileName

     )
     $Name
     ## Normal Script Code goes here
     }

In the above in the param() block a mandatory named argument is specified and has a ValidateScript attribute.

Everything looks great but the function needs to be very restrictive and it should fail when the parameter is not supplied to the function.
Right now because of making the parameter mandatory , I get a prompt to supply the value as below :



But I wanted my function to fail if the parameter is not specified cause I want to run my function non-interactively. 

I knew that the throw statement can be used to achieve this. Below is the info from about_throw topic.

USING THROW TO CREATE A MANDATORY PARAMETER

   You can use the Throw keyword to make a function parameter mandatory.

   This is an alternative to using the Mandatory parameter of the Parameter
   keyword. When you use the Mandatory parameter, the system prompts the user
   for the required parameter value. When you use the Throw keyword, the
   command stops and displays the error record.

So I used the throw statement to achieve what I needed, instead of the Mandatory Named Argument  :)

function Test-AdvancedFunction {
     [CmdletBinding()]
      param(
           [Parameter(Position =0 )]
           [ValidateScript({ Test-path $_})]
           [System.String]
           $FileName = $(throw "Filename not specified")

     )
      $FileName
      ## Normal Script Code goes here
     }


Now I can use validation as well as exit the function when the parameter is not specified  :

So in this way we can have a mandatory parameter which will exit the script/function if the parameter is not supplied while invoking the script/function rather than prompting user for the info.

Sunday, July 07, 2013

Drop to PowerShell using Remotely Anywhere

This is just basics but something I came across recently ,  remotely anywhere is a remote administration tool which our Enterprise uses.
Most of the time User face some issues with installation of applications , so Remotely anywhere helps in assisting them using the remote desktop using a web interface

Remotely anywhere also listens on port 22 the name of the service is "RemotelyAnywhere SSH Server".

Sojust connect to a remote machine using putty and then you are droppped to a  cmd shell  and then start PowerShell from there. The best part is it doesn't prompt for the User confirmation :)


The pre-requisite is that Remotely Anywhere be installed on the remote machine and listening on port 22.

So open up a putty console and connect on port 22.



After that you need to authenticate yourself, I am logging into a machine in Enterprise (running AD) so I used my domain credentials to log in and you do need to


After that you are dropped to a command prompt, from where you can run powershell.exe for swift and easy administration :)





Now the difference between the opened session in the above screenshot is that the Profile gets executed when I open this session which is equivalent to running this powershell session locally on that machine.

Note : When you open a PSSession the profile doesn't run for the User.

So now for common tasks to be performed behind the scene for Users like installing a SCCM client, repairing it can be done without disrupting User's day to day work.






Monday, February 11, 2013

Use PowerShell to Create New AD Users using a Template

To use and existing account as a template to create new users one would use the good old "Active Directory Users and Computers" , right? by right-clicking on the User to be used as template and selecting "Copy" which will prompt something like below:



But this blog is  meant for doing things using PowerShell.

To quickly get me started I was tempted to use "Active Directory Administrative Center" on Server 2012, so that I could see the PowerShell history for the my actions( Yeah! you can do that now !!) but there was no method to do that in AD Admin Center :O , See below

 

The User "Dexter POSH" is the member of the group "RemotePOSHAdmins" under the OU "POSHAdmins" in my domain. I want to add a new User here using the dexterposh user account as the template.

At first I thought of simply getting the User information using Get-ADUser and piping it into New-ADUser cmdlet (because it accepts pipeline input of type "None or Microsoft.ActiveDirectory.Management.ADUser" and Get-ADUser spits out object of the specified type) , but see below it fails :


See that I used -whatif parameter here to be cautious :)

Now what to do let's go and ask updated Get-Help. After going through help carefully I came to know that the correct parameter to use here is -Instance . So quick info on that is below:




Voila now I know how it will work :)

So I tried



But it failed probably because the "dexterposh" account is enabled and while creating a new user I didn't specify the password. So what I will do is disable it by default while creating it:


So everything worked , I noticed that you need to specify the -path to the desired OU otherwise by default the account is created in "Users" in the domain.

Now this can be used to automate creation of Users by using different templates. What we can do is create a CSV with required information for Account Creation like first name, last name etc and then a field specifying if it needs to copied from an existing account say "tocopy" which will be set to the SamAccountName of the account to be used as template and create the new users using above method.


Monday, February 04, 2013

Winter Scripting Camp 2013 - #1 Advanced Problem


First a bit background on the Winter Scripting Camp, this is the first time a Scripting camp is happening. The sole purpose of this is to make the scripters all over the world sharpen their skills for the upcoming "Scripting Games 2013". These games are awesome..so much stuff to learn and personally what I like about them are they are very practical and work-oriented whatever the problems asked are very real-life PRODUCTION environment problems. This develops your approach towards the problems which you might face later in your Work Environment. I learned a whole lot of lessons while participating for last year's Scripting Games in Beginner's category. Hopefully, the Scripting Camp will make me a bit sharper this time :P


Following is the first problem of the Winter Scripting Camp...See how practical the problem is :P

#1 Advanced Section
Sizing up the Disks

You have been asked to create a Windows PowerShell advanced function named Get-DiskSizeInfo. It must accept one or more computer names, and use WMI or CIM to query each computer. For each computer, it must display the percentage of free space, drive letter, total size in gigabytes, and free space in gigabytes. If a specified computer cannot be contacted, the function must log the computer name to C:\Errors.txt and display no error message.



Below is my submission. The formatting is a bit buggy . I will edit the post later on mistakes I did here and what I learned:

<#
.Synopsis
  Gets the Disk Size Info
.DESCRIPTION
  Uses WMI to query the disk size info, then spits out custom Objects with
  required properties
.EXAMPLE
  "Server1","server2" | Get-DiskSizeInfo  -Verbose
.EXAMPLE
  Get-DiskSizeInfo -ComputerName "server1","server2"  -ErrorLogFile D:\errors.txt
  This example shows how to run this function against a list of computers and specifying
  an alternate log file for computers not online.
.INPUTS
  [system.string[]]
.OUTPUTS
  [System.Management.Automation.PSCustomObject]
#> 
function Get-DiskSizeInfo 
{ 
  [CmdletBinding()] 
  [OutputType([psobject])] 
  Param 
  ( 
  # Param1 help description 
    ValueFromPipelineByPropertyName=$true, 
  ValueFromRemainingArguments=$false, 
  Position=0 )> 
  [ValidateNotNullOrEmpty()] 
  [Alias("CN","server")] 
  [string[]]$ComputerName=[environment]::MachineName , 

  # Param2 help description 
   
  [ValidateScript({Test-Path -path (split-path -parent $_) -pathtype container })] 
  [string] 
  $ErrorLogFile="C:\Errors.txt" 

  ) 
  Process 
  { 
  Foreach ($computer in $ComputerName) { 
  if (Test-Connection -ComputerName $Computer -Count 2 -Quiet){ 
  Write-Verbose "$computer is online...getting disk size info" 
 
  try { 
  $wmidiskinfo = Get-WmiObject -Class Win32_LogicalDisk -Filter “DriveType = 3" -ErrorAction stop 
  foreach ($disk in $wmidiskinfo) { 
  New-Object -TypeName PSObject -Property @{ 
  ComputerName=$computer 
  Drive=$disk.DeviceID 
  Size=$disk.Size/1GB 
  'FreeSpace(in GB)'=$disk.Freespace/1GB 
  'FreeSpace(%)'=($disk.Freespace/$disk.Size) * 100 -as [int] } 
  } 
  } 
 
 
  catch { 
  Write-Verbose "Error while getting WMI Object or Creating PSObject for $computer" 
  $Error[0].Exception 
  } 
  }#end if block 
  else { 
  Write-Verbose "$computer is not online..logging" 
  if (Test-Path $ErrorLogFile){ 
  $computer | Out-File -FilePath $ErrorLogFile -Append 
  } 
  else{ 
  $Computer | Out-File -FilePath $ErrorLogFile 
  } 
  } 
 
  }#end foreach outer 
 
  }#end Process 
}#end Function

Friday, January 18, 2013

Enable Auditing KCC in ServerCore


In a Windows Environment running Active Directory, Knowledge Consistency Checker (KCC) is responsible for creating connection objects and builds replication topology which is at the heart of Multi-Master Replication.

To audit if the KCC is up and running or actually the replication changes are occurring, We can enable auditing by changing a Registry Entry. But as I have a ServerCore running a Domain Controller I will do this on it

First Create a New PowerShell Session and then enter it.
Most of you will be familiar with PSDrives which are nothing but an abstraction which helps you see various datastores like Registry as Drives.




Now the HKLM:\ is the Drive for HKEY_LOCAL_MACHINE registry hive.
HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics is the Registry key where we need to change the "1 Knowledge Consistency Checker" property to any value between 0-5 where 0 is no auditing and 5 is the  maximum auditing messages. I will select 2 just in case.
Go ahead and change the registry as shown below (or any other method like remote registry)



This will just enable auditing no reboot required. Unfortunately, I think I can't test it as I have only One DC running in my test environment. But good to know this.

Tuesday, January 08, 2013

Enable-RemoteDesktop -computername DexServerCore

Now, Basically why to enable remote desktop on a ServerCore ? When you have already WinRM and PowerShell remoting already kicking.

Well, I honestly can't say why to enable it...cause I have the ServerCore machine sitting up on my Hyper-V Server and I could "connect" to it as easily. But it's good to know that it could be done.

With Server 2012 you could add a GUI or a Servercore...but there is something called Minimal Server Interface as well in Server 2012 (plus Server 2012 gives you ability to switch between all these installations too). So you might want to read up on this here . So a use scenario can be to add Minimal Server Interface on a remote host for the time being , Enable Remote Desktop  and perform Admin tasks which occur rarely.

How to Enable Remote Desktop ?

Well from what I could understand there are 2 registry entries which control this.
1. fDenyTSConnections under  'HKLM:\System\CurrentControlSet\Control\Terminal Server'. This Reg Entry tells if the Remote Desktop Connections are allowed to the Machine are not.


2. UserAuthentication under 'HKLM:\System\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' . This Reg Entry if set to 1 indicates that "Only Secure Connections are allowed" and if set to 0 then it means "All Connections allowed".



Now , all the cool PowerShell kids (Guess I am one..lol ) would just open a PSSession to the remote machine and set these Reg Entries properly to allow Remote Desktop Connection. Legacy method would be to use remote registry to edit entries, but there is yet another way.....I stumbled across it recently on someone's blog (sorry! don't remember the blog address).




The above method is using WinRM to essentially call the scregedit.wsf script on remote machine dexservercore from my local machine.

Once done, fire up the mstsc.exe and connect to the remote machine, it prompts for the credentials.




So here it is Remote Desktop working on your Server Core.