Advanced Windows PowerShell Scripting Video Training

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

Friday, September 23, 2016

ByPropertyName: The Good Stuff

This is post 6 of 7 in this series.

When we pass information ByPropertyName, we maintain very good control of what parameters our information binds to. With ByValue, if you sent a string of text, the information presented to the receiving cmdlet is whatever the text was.  With ByPropertyName, the properties of the object are examined.

Let’s take a look at the help file for New-SMBShare.  I’m only going to show the parameters that accept pipeline input ByPropertyName.

-Name
        Specifies a name for the new SMB share. The name may be composed of any valid
        file name characters, but must be less than 80 characters in length. The names
        pipe and mailslot are reserved for use by the computer.
       
        Required?                    true
        Position?                    2
        Default value               
        Accept pipeline input?       True (ByPropertyName)
        Accept wildcard characters?  false

-Path
        Specifies the path to the location of the folder to share. The path must be fully
        qualified; relative paths or paths that contain wildcard characters are not
        permitted.
       
        Required?                    true
        Position?                    3
        Default value               
        Accept pipeline input?       True (ByPropertyName)
        Accept wildcard characters?  false

-ScopeName
        Specifies the scope name of the share.
       
        Required?                    false
        Position?                    4
        Default value               
        Accept pipeline input?       True (ByPropertyName)
        Accept wildcard characters?  false


What the help file is telling us is that if we have an object with a Name property containing string data, that data will bind to the –Name parameter of New-SMBShare. The same if the object has a property called Path and Scope.  Here are the rules for passing parameters ByPropertyName
  1. Does the object in the pipeline have property names that match the parameter names of the receiving cmdlet?
  2. Do those matching parameters accept pipeline input ByPropertyName?
  3. Do the property and parameter datatypes match?

Let’s create an object that has two properties.  One called Name and the other called Path.  Both will contain string data.

$Obj = New-Object -TypeName PSObject -Property @{
    Name = "MySMBShare"
    Path= "C:\PS"
}


To see the contents of this object, just ask for the variable $Obj.
PS C:\> $Obj

Name       Path
----       ----
MySMBShare C:\PS

We have satisfied the requirement of having an object that contains the same name and data types as the parameters of New-SMBShare that can accept pipeline input ByPropertyName.
Here is a look at my current SMB shares
PS C:\> Get-SmbShare

Name   ScopeName Path                              Description   
----   --------- ----                              -----------   
ADMIN$ *         C:\WINDOWS                        Remote Admin  
C$     *         C:\                               Default share 
D$     *         D:\                               Default share 
E$     *         E:\                               Default share 
IPC$   *                                           Remote IPC    
print$ *         C:\WINDOWS\system32\spool\drivers Printer Drivers

Now we are going to pipe our object to New-SMBShare
PS C:\> $Obj | New-SMBShare

Name       ScopeName Path  Description
----       --------- ----  -----------
MySMBShare *         C:\PS   

Let’s take a look at my SMB shares now.
PS C:\> Get-SmbShare

Name       ScopeName Path                              Description   
----       --------- ----                              -----------   
ADMIN$     *         C:\WINDOWS                        Remote Admin   
C$         *         C:\                               Default share 
D$         *         D:\                               Default share 
E$         *         E:\                               Default share 
IPC$       *                                           Remote IPC    
MySMBShare *         C:\PS                                           
print$     *         C:\WINDOWS\system32\spool\drivers Printer Drivers


The graphic below shows how the properties of the object  are passed to the parameters of New-SMBShare ByPropertyName.  The –Scope parameter of New-SMBShare did not have a corresponding property in the object so its value is NULL.


This is what this command would look like without using the PowerShell pipeline.

PS C:\> New-SMBShare -Name MySMBShare -Path C:\PS

Name       ScopeName Path  Description
----       --------- ----  -----------
MySMBShare *         C:\PS 



Thursday, September 22, 2016

ByValue for Specific Objects

This is post 5 of 7 in this series.
Many cmdlets accept pipeline input ByValue, but only for very specific objects.  After all, do you think it would be wise to do this?

PS C:\> Get-Process | New-Mailbox

Probably not. We are going to see if this command will work.

PS C:\> Get-Service -Name "Bits" | Stop-Service

First off, let’s get the objects TypeName that is produce by the left hand side of the command.

PS C:\> Get-Service -Name "Bits" | GM


   TypeName: System.ServiceProcess.ServiceController

Name                      MemberType    Definition                                        
----                      ----------    ----------                                        
Name                      AliasProperty Name = ServiceName                                
RequiredServices          AliasProperty RequiredServices = ServicesDependedOn              
Disposed                  Event         System.EventHandler Disposed(System.Object, Syst..

We can see that the object is of type ServiceController. Next, we need to see if any parameter of Stop-Service will accept input from the PowerShell pipeline.

PS C:\> Get-help Stop-Service -Parameter *

I shortened the list to only the parameters that meet this criteria.

-InputObject
    Specifies ServiceController objects that represent the services to stop. Enter a
    variable that contains the objects, or type a command or expression that gets the
    objects.
   
    Required?                    true
    Position?                    1
    Default value                none
    Accept pipeline input?       True (ByValue)
    Accept wildcard characters?  false
   

-Name
    Specifies the service names of the services to stop. Wildcard characters are permitted.
   
    Required?                    true
    Position?                    1
    Default value                none
    Accept pipeline input?       true(ByValue,ByPropertyName)
    Accept wildcard characters?  false

There are only two parameters where theAccept pipeline input attribute is True.  Now we need to look at the object types that each parameter will accept. Remember, we are passing a ServiceController object to this cmdlet.  The –Name parameter only accepts String objects so no go there. The –InputObject parameter does accept our ServiceController object.  That is why we are able to execute
PS C:\> Get-Service -Name "Bits" | Stop-Service





Wednesday, September 21, 2016

ByValue for Generic Objects

This is post 4 of 7 in this series.

Some cmdlets are designed to work with any object.  Take a look at the –InputObject parameter of Sort-Object.

-InputObject []
    Specifies the objects to sort.
   
    When you use the InputObject parameter to submit a collection of items, Sort-Object
    receives one object that represents the collection. Because one object cannot be
    sorted, Sort-Object returns the entire collection unchanged.
   
    To sort objects, pipe them to Sort-Object.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       true (ByValue)
    Accept wildcard characters?  false

Whenever you see a parameter accepting objects of type Object or PSObject, then you know that this cmdlet can handle anything. Let’s take a look at the results of Get-SMBShare without using Sort-Object

PS C:\> Get-SmbShare

Name   ScopeName Path                              Description   
----   --------- ----                              -----------   
ADMIN$ *         C:\WINDOWS                        Remote Admin  
C$     *         C:\                               Default share 
D$     *         D:\                               Default share 
E$     *         E:\                               Default share 
IPC$   *                                           Remote IPC    
print$ *         C:\WINDOWS\system32\spool\drivers Printer Drivers

Just by looking at it, the cmdlet sends the objects into the pipeline sorted alphabetically on the Name property.  Now let’s pipe it to Sort-Object.

PS C:\> Get-SmbShare | Sort-Object

Name   ScopeName Path                              Description   
----   --------- ----                              -----------   
ADMIN$ *         C:\WINDOWS                        Remote Admin  
C$     *         C:\                               Default share 
D$     *         D:\                               Default share 
E$     *         E:\                               Default share 
IPC$   *                                           Remote IPC    
print$ *         C:\WINDOWS\system32\spool\drivers Printer Drivers

The results are the same.  Well, that was useful.  The thing that we forgot to do was to tell Sort-Object how to sort our objects.

PS C:\> Get-SmbShare | Sort-Object -Property Description

Name   ScopeName Path                              Description   
----   --------- ----                              -----------   
C$     *         C:\                               Default share 
D$     *         D:\                               Default share 
E$     *         E:\                               Default share 
print$ *         C:\WINDOWS\system32\spool\drivers Printer Drivers
ADMIN$ *         C:\WINDOWS                        Remote Admin  
IPC$   *                                           Remote IPC   


For this cmdlet to do anything, you need to give it some instructions.  In this case, which property to sort on. Sort-Object has been around since PowerShell v1.0.  Get-SMBShare was introduced in PowerShell v3.0, but only if you are running Windows 8 or newer.  In other words, the author of the Sort-Object cmdlet had no idea that the cmdlet Get-SMBShare would ever be created.  The author simple coded the Sort-Object cmdlet to accept all objects. Because of this, we can continue to sort objects evening with PowerShell v5.1.

Tuesday, September 20, 2016

The PowerShell Pipeline at the Surface: ByValue

This is post 3 of 7 in this series.

In reality, we are not actually passing objects to the cmdlet on the right, rather we are passing objects to the parameters of the cmdlet on the right. There are two ways that parameter passing can be achieved; ByValue and ByPropertyName.

In ByValue parameter passing, we actually look at the value of the object.  For example, if we pass a string object to a cmdlet that can receive the object ByValue, it looks that what the string says.  In other words, if the string says “Hello World”, the cmdlet will use “Hello World”. To determine how parameter passing will work, or even if it will, we need to perform two tasks. 

Task number 1 is to discover the TypeName of the object the was created from the cmdlet on the left.  This can be accomplished in a couple of ways.  The first way is to pipe the object to the cmdlet Get-Member.

PS C:\> "Bits" | Get-Member


   TypeName: System.String

Name             MemberType            Definition                                            
----             ----------            ----------                                           
Clone            Method                System.Object Clone(), System.Object ICloneable.Clo...
CompareTo        Method                int CompareTo(System.Object value), int CompareTo(s...
Contains         Method                bool Contains(string value)              

When you do, take a look at the TypeName.  In this case, the TypeName is System.String.  We will just say that it is a string object for short.

Another way to get the TypeName is to use the GetType() method.

PS C:\> ("Bits").GetType()

IsPublic IsSerial Name                                     BaseType                       
-------- -------- ----                                     --------                        
True     True     String                                   System.Object    

Next, we need to take a look at the full help file for the cmdlet we are sending this string object to.  In our scenario, we are going to pass our string “Bits” to Get-Service.  Let’s look at the Parameter section of the help file for Get-Service.

PS C:\> Get-help Get-Service -Parameter *

-ComputerName []
    Gets the services running on the specified computers. The default is the local
    computer.
   
    Type the NetBIOS name, an IP address, or a fully qualified domain name (FQDN) of a
    remote computer. To specify the local computer, type the computer name, a dot (.), or
    localhost.
   
    This parameter does not rely on Windows PowerShell remoting. You can use the
    ComputerName parameter of Get-Service even if your computer is not configured to run
    remote commands.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       true (ByPropertyName)
    Accept wildcard characters?  false
   

-DependentServices []
    Indicates that this cmdlet gets only the services that depend upon the specified
    service.
   
    By default, this cmdlet gets all services.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       false
    Accept wildcard characters?  false
   

-DisplayName
    Specifies, as a string array, the display names of services to be retrieved. Wildcards
    are permitted. By default, this cmdlet gets all services on the computer.
   
    Required?                    true
    Position?                    named
    Default value                none
    Accept pipeline input?       false
    Accept wildcard characters?  false
   

-Exclude []
    Specifies, as a string array, a service or services that this cmdlet excludes from the
    operation. The value of this parameter qualifies the Name parameter. Enter a name
    element or pattern, such as "s*". Wildcards are permitted.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       false
    Accept wildcard characters?  false
   

-Include []
    Specifies, as a string array, a service or services that this cmdlet includes in the
    operation. The value of this parameter qualifies the Name parameter. Enter a name
    element or pattern, such as "s*". Wildcards are permitted.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       false
    Accept wildcard characters?  false
   

-InputObject []
    Specifies ServiceController objects representing the services to be retrieved. Enter a
    variable that contains the objects, or type a command or expression that gets the
    objects. You can also pipe a service object to this cmdlet.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       true (ByValue)
    Accept wildcard characters?  false
   

-Name []
    Specifies the service names of services to be retrieved. Wildcards are permitted. By
    default, this cmdlet gets all of the services on the computer.
   
    Required?                    false
    Position?                    1
    Default value                none
    Accept pipeline input?       true(ByValue,ByPropertyName)
    Accept wildcard characters?  false
   

-RequiredServices []
    Indicates that this cmdlet gets only the services that this service requires.
   
    This parameter gets the value of the ServicesDependedOn property of the service. By
    default, this cmdlet gets all services.
   
    Required?                    false
    Position?                    named
    Default value                none
    Accept pipeline input?       false
    Accept wildcard characters?  false
    

In the above help file, look at the Accept pipeline input attribute on each parameter.  Do any of them have a value of True? Two of them do.  –Name and –ComputerName both accept pipeline input.  Does one of these accept pipeline input ByValue?  Only the –Name parameter does.  As a matter of fact, it accepts input both ByValue and ByPropertyName.  When you are faced with this scenario, ByValue is always tried first.

Next, we need to look at the data type of the –Name parameter.

PS C:\> Get-Help Get-Service -Parameter Name

-Name []
    Specifies the service names of services to be retrieved. Wildcards are permitted. By
    default, this cmdlet gets all of the services on the computer.
   
    Required?                    false
    Position?                    1
    Default value                none
    Accept pipeline input?       true(ByValue,ByPropertyName)
    Accept wildcard characters?  false

Right after the parameters name, you will see the data type []. In other words, the –Name parameter of Get-Service will accepts a string object’s value if passed to it from the PowerShell pipeline.  Let’s give it a try.

PS C:\> "Bits" | Get-Service

Status   Name               DisplayName                          
------   ----               -----------                          
Running  Bits               Background Intelligent Transfer Ser...

As you can see, it works!  This is what it looks like without using the pipeline.

PS C:\> Get-Service -Name "Bits"

Status   Name               DisplayName                          
------   ----               -----------                          
Running  Bits               Background Intelligent Transfer Ser...

Here is a summary of the steps that we took to determine if an object will be accepted ByValue by another cmdlet.

  1. Take the object in the pipeline from the cmdlet on the left and discover its TypeName with Get-Member
  2. Open the full help file of the receiving cmdlet on the right and see if any of its parameters accept input from the PowerShell pipeline ByValue.
  3. If they do accept pipeline input ByValue, do they accept the same object type that you are passing to it? If so, the passing ByValue will work.


1