Advanced Windows PowerShell Scripting Video Training

Advanced Windows PowerShell Scripting Video Training
Advanced Windows PowerShell Scripting Video Training

Thursday, October 31, 2013

How Many ??? is in PowerShell?

When I start a PowerShell class, I am often asked “How many cmdlets are there in PowerShell?”  Well, the answer is “That depends.”  Once we start looking at cmdlets, I am often asked “How many parameters does a cmdlet have?”  The answer is “that depends.”  I also get asked “How many modules are there?” Once again, “that depends.”  This is not the answer that paying clients want to here, but it is the truth.

How many cmdlets are there? It depends on what is available to the client/server you are working on. Also, did you add your own modules?

Get-Command | Measure-Object | Select-Object –ExpandProperty Count

How many modules are there?  What technologies are installed on the local client/server?  Again this is a variable that I cannot answer. 

Get-Module –ListAvailable | Measure-Object | Select-Object –ExpandProperty Count

How many parameters are there?  That depends on which cmdlet you are using.  Utilizing the Help system will expose the different parameters that are available to you for individual cmdlets.

To get a total count of them all parameters is a bit more complex.  This code below will help you out with that.  Be forewarned, this might take a while.

$Total = 0

ForEach ($C in (Get-Command))

{

    $Count++

    $Status = (($Count/($Commands.count))*100)

    $ProgHash = @{'Activity' = "Processing Commands"

                  'PercentComplete' = $Status

                  'Status' = "$Status % Completed"}

    Write-Progress @ProgHash

    Try

    {

        $Total += (Get-Command $C -ErrorAction Stop).Parameters |

            Select-Object -ExpandProperty Keys |

            Measure-Object |

            Select-Object -ExpandProperty Count

    }

    Catch

    {

        $Total += 0

    }

 

}

Write-Output $Total

Here are the stats for my client:

Cmdlets: 3837

Modules: 80

Parameters: 95405

Wednesday, October 30, 2013

How to address Property Names with Spaces in them?

Things don’t always go as planned.  For example when my wife and I moved from Indiana to Arizona this spring.  We were planning on taking three days to drive the dogs from Indianapolis to Phoenix.  I was at the PowerShell Summit in Redmond and she was home finishing the packing.  I could tell by the way she was sounding on the phone that things were not going well so I jumped on an early flight to come home.  Well….I landed in Houston to find the plane was broken and no flights left to Indianapolis.  The airline was planning on getting me home around 2:30 PM the next day. Um,…no.  So I took a plane to Chicago that night and slept in the USO and got home at 10 AM to help my wife.  Sometimes you have to turn a challenge into an opportunity.

Sometimes you need to work with data that was never indented to be used with PowerShell.  This can present a challenge.  Those of you who have taken my PowerShell classes know that I frown on any variable that has a space in it.  Take a look at what you have to do to get a variable with a space in it to work.

PS C:\> ${My Variable} = "No a good way to do things"

${My Variable}

No a good way to do things

To get this to work, we have to encapsulate the variable name inside of curly brackets. This is not at all easy to read so I discourage it.

While working on a Group Policy issue, I needed a way to extract information from the Group Policy Settings Reference for Windows and Windows Server. This is an excel spread sheet.  I took the Administrative Templates sheet and saved it as an CSV file.  Once I did that, I could import in each record and use PowerShell to present GPO settings that fit my criteria.  Well, let’s import this data in and take a look at the property names.

PS C:\> Import-Csv -LiteralPath "C:\Users\JASON\Documents\PowerShell\InDevelopment\GPO\AdminTemplates2012.csv" |

    Get-Member -MemberType Properties |

    Select-Object -Property Name

 

Name

----              

Active Directory Schema or Domain Requirements                                                                       

File Name

Help Text

Logoff Required

Policy Path        

Policy Setting Name                                                                                                  

Reboot Required                      

Registry Information

Scope                                                  

Status

Supported On                                                             

Wow, not good. We have a lot of property names with spaces in them.  We have two choices here.  We can either just use the property names as is or we can modify them. 

Using them as is.

PS C:\> Import-Csv –LiteralPath "C:\AdminTemplates2012.csv" |

    Where {"$PSItem.Policy Path" -like "*Windows Components\Remote Desktop Service*"}

In the Where-Object line, we used double quotes surrounding the entire call to the object and the Policy Path” property.  Not as bad as using the curly brackets that we have to do for variables.  This will work.  For those of you who prefer to not uses spaces, we simple need to add some new properties to the object while getting ride of the old ones.

 

PS C:\> Import-Csv -LiteralPath "C:\AdminTemplates2012.csv" |

    ForEach-Object -Process {

        $Props = $PSItem | Get-Member -MemberType Properties | Select-Object -ExpandProperty Name

        $Obj = New-Object -TypeName PSObject

        ForEach ($P in $Props)

        {

            $Hash = @{NotePropertyName =  $P.Replace(" ","")

                      NotePropertyValue = $PSItem.$P}

            $Obj | Add-Member @Hash

        }

        Write-Output $Obj

        }

Yes, I know, a bit much.  Through this into a function and reuse it with other objects that suffer from spaces in the property names if you need to.  Really what we are achieving is creating a whole new object from the object created by Import-CSV.  This is one continuous command.  We are using ForEach-Object to cycle through one object at a time for processing.  The $Props variable stores the property names of each property in the object.  We then create a new object to pass out into the pipeline.  The NotePropertyName simply takes the name of the property being handled and removes all spaces from it.  The NotePropertyValue is the value of the original property.  Once all the properties have been renamed, the new object is sent to the pipeline so you can do what ever you need to do without the spaces.

Either way will work, you just need to choose which way you want to handle this “opportunity”.

Monday, October 28, 2013

I’ll be Speaking at the 2014 PowerShell Summit!

Good afternoon PowerShell fans.  Last week I found out that I will have the honor speaking to all of you at the 2014 PowerShell summit.  I’m be presenting a session titled “Empower your Help Desk with PowerShell”.  The PowerShell summit is a great way to learning new skills from the industry’s top PowerShell experts.  You can register at PowerShell.org.  This summit always sells out so don’t wait to long to register.

I’ll see you all in April,

Jason

Tuesday, October 15, 2013

Use PowerShell to discover all computer objects in AD

I’m sitting in my classroom while my class is doing a lab and answering some questions from PowerShell.com.  This scripter needs to discover all the computer objects in Active Directory with an empty description and provide a setting for it.

1

2

3

4

5

Import-Module ActiveDirectory # For PowerShell V2 Compatibility

$Desc = "New Description String"

Get-ADComputer -Filter * -Properties Description |

    Where-Object {$_.Description -eq $Null} |

    Set-ADComputer -Description $Desc

 

Line 1 imports the Active Directory module into this session.  You can omit this line if you are running PowerShell V3 or V4 and have the PowerShell ActiveDirectory module installed locally.

Line 2 is used to provide the new description property value.

Line 3 gets all the computer objects from Active Directory.  I was not able to get a filter for a description that is NULL.  Hence the reason for line 4 which uses Where-Object to filter on the NULL Description property.  Normally I would aviod using Where-Object whenever possible.

Line 5 sets the new property value.

Asking my class, we have an average of 500 clients per student.  We timed this task to take approximately 8 seconds per client to perform this operation manually using Active Directory Users and Computers.  Non-stop and without errors, this operation would take just over an hour to complete.  In our test environment, this process took 12 seconds to complete the change on 500 clients. Not bad for a simply script.

Thursday, October 3, 2013

Discover Who is Logged Onto Your Clients

Recently I helped an IT Pro on PowerShell.com who needed to find out how to discover who was logged into his clients.  Here is the advanced version of the code that allows you to send information via the PowerShell Pipeline.

This code was tested in a PowerShell V3 environment.  If you need to alter the code for a PowerShell V2 without WinRM turned on, make the following changes:

Line 50: "ClassName" to "Class"
Line 58: "Get-CIMInstance" to Get-WMIObject"


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151

Function Get-LoggedOnUsers
{
[cmdletBinding()]
    Param (
        [parameter(Mandatory=$true,
         ValueFromPipelineByPropertyName=$true,
         ParameterSetName="Object")]
        [String[]]
        $ComputerName,
        [Parameter(Mandatory=$true,
         ValueFromPipeline=$true,
         ParameterSetName="Name")]
         [String[]]
         $Name
        )

    Begin
    {
        Function CustomObj
        {
            # Create the empty object.
            $Obj = New-Object -TypeName PSObject -Property @{
                'User' = $Null
                'Computer' = $Null
                'Online' = $Null}

                # Provide the object with a custom type name.
            $Obj.psobject.TypeNames.Insert(0, "LoggedOnUser")
           
            # Send the object to the calling statement.
            Write-Output $Obj
        }
    }
    # This is an advance function.
    Process
    {
       
        # If the "Name" parameter is used, copy
        # its contents to the "ComputerName"
        # Parameter.
        If ($Name -ne $Null)
        {$ComputerName = $Name}

        # Cycle through each item in "ComputerName".
        ForEach ($Computer in $ComputerName)
        {   
            Try
            {
                # Hash value used with Get-CIMInstance.
                $CIMHash = @{'ClassName' = "Win32_ComputerSystem";
                             'ComputerName' = $Computer;
                             'EA' = "Stop"}
               
                # Create the object to output.
                $Obj = CustomObj

                # Plug in the data ito the blank object.
                $Obj.User = (Get-CimInstance @CIMHash |
                    Select-Object -ExpandProperty UserName)
                $Obj.Computer = $Computer
                $Obj.Online = $True
          
            }
            Catch
            {
                # Create the object to output.
                # This is the output if no user
                # is currently logged into the
                # target client.
                $Obj = CustomObj
                $Obj.User = "n\a"
                $Obj.Computer = $Computer
                $Obj.Online = $False
            }

            Finally
            {
                # Send the object to the pipeline.
                Write-Output $Obj
            }
         }
    } # End: Process
 
<#
.SYNOPSIS
Returns a list of the users logged into clients targeted by
this cmdlet.

.DESCRIPTION
Returns a list of the users logged into clients targeted by
this cmdlet.  If the user name is listed as " ", then there
is not a user currently logged on.

===============================================================================
== Cmdlet: Get-LoggedOnUsers                                                 ==
== Module: PSV3                                                              ==
==---------------------------------------------------------------------------==
== Author: Jason A. Yoder                                                    ==
== Company: MCTExpert, Inc.                                                  ==
== Blog: MCTExpert.Blogspot.com                                              ==
== Twitter: @JasonYoder_MCT                                                  ==
==---------------------------------------------------------------------------==
== License Information:                                                      ==
== Copyright 2013 - MCTExpert, Inc.                                          ==
== This code is licensed for personal use only.  This code may not be        ==
== re published or distributed in whole or in part without the express       ==
== written consent of MCTExpert, Inc.  All rights reserved.                  ==
==---------------------------------------------------------------------------==
== Disclaimer: The user takes full responsibility for the execution of any   ==
== PowerShell code.  This code is provided without warranty or support.      ==
== As with all PowerShell code, review it and test it in a test environment  ==
== prior to using it in a production environment.  The user takes complete   ==
== responsibility for the results of executing this code.                    ==
===============================================================================

.PARAMETER ComputerName
The names of the computers to return the currently logged
on user.  Use this when passing objects via with a
ComputerName property

.PARAMETER Name
The names of the computers to return the currently logged
on user.

.EXAMPLE
"CL-154", "CL-155", "CL-156"  | Get-LoggedOnUsers

User                 Computer
----                 --------
INDNet\Administrator CL-154 
INDNet\Administrator CL-155 
N\a                  CL-156

.EXAMPLE
Get-ADComputer -filter * |
    Select -Property @{N="ComputerName";E={$PSItem.Name}} |
    Get-LoggedOnUsers

Extracts all the clients from Active Directory and returns
the logged on user.

.EXAMPLE
Get-LoggedOnUsers -Name "CL-154", "CL-155", "CL-156"

User                 Computer
----                 --------
INDNet\Administrator CL-154 
INDNet\Administrator CL-155 
N\a                  CL-156
#>
} # End: Function Get-LoggedOnUsers






Lines 4 – 15 define our two parameters of $ComputerName and $Name.  A parameter set is used so that these two parameters cannot be used at the same time.  $ComputerName is designed to be used from a object that contains a property called “ComputerName”.  $Name is designed to accept a string of names.
Line 17 – 34 is our begin block.   It contains a function that creates a new, empty object.  Line 28 assigned a typename to our object.  This method of creating a object is done to make updating the object with new properties easier and also to  reduce clutter when populating the objects properties.
Lines 35 – 82 executes for each object passed to this cmdlet.
Lines 41 and 42 ensure that no matter which parameter is used, the data is processed.
Lines 45 – 81 loops through each client.
Lines 47 and 64 are the Try..Catch blocks that will handle any clients that are not online.  A possible error may occur on line 58 where the script reaches out to the remote client.  If the client is online, the currently logged on user name is extracted.  If it is not online, the CATCH block executes and populates the object with user “n\a” and Online = $False.
Line 76 starts the FINALLY block which will execute no matter if a connection error occurred or not.  It wends the object to the PowerShell pipeline.