PowerShell for Windows Admins


December 21, 2014  7:49 AM

Delivering PowerShell code with the November preview

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Step one of investigating OneGet and PowerShellGet – install the latest preview.  I have the environment I used for the demos at the European PowerShell Summit with a machine set up for DSC. Its currently running the September preview:

£> $PSVersionTable

Name                           Value
—-                           —–
PSVersion                      5.0.9814.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34014
BuildVersion                   6.4.9814.0
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.2

 

The November preview is supposed to install over the top of the September preview. I’m installing on to  Windows 2012 R2 so I want:

WindowsBlue-KB3006193-x64.msu

The target machine is fully patched apart from the November Windows 2012 R2 roll up.

Installation occurred with out a problem. – I like the change to being able to just install over the top of pervious versions.  After the usual restart the version has changed.

£> $PSVersionTable

Name                           Value
—-                           —–
PSVersion                      5.0.9883.0
WSManStackVersion              3.0
SerializationVersion           1.1.0.1
CLRVersion                     4.0.30319.34014
BuildVersion                   6.4.9883.0
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.2

The PowerShellGet module contains these cmdlets:

Find-Module
Get-PSRepository
Install-Module
Publish-Module
Register-PSRepository
Set-PSRepository
Unregister-PSRepository
Update-Module

The module type is Script meaning it contains PowerShell Advanced functions.  You can see the code at C:\Windows\System32\WindowsPowerShell\v1.0\Modules\PowerShellGet

Just make sure you don’t change anything.

First thing is to see what repositories we’ve got:

£> Get-PSRepository | fl
Name               : PSGallery
SourceLocation     : https://www.powershellgallery.com/api/v2/
Trusted            : False
Registered         : True
InstallationPolicy : Untrusted
OneGetProvider     : NuGet
PublishLocation    : https://go.microsoft.com/fwlink/?LinkID=397527&clcid=0x409
ProviderOptions    : {}

Name               : MSPSGallery
SourceLocation     : http://search.microsoft.com/default.aspx
Trusted            : True
Registered         : True
InstallationPolicy : Trusted
OneGetProvider     : NuGet
PublishLocation    : http://go.microsoft.com/fwlink/?LinkID=397635&clcid=0x409
ProviderOptions    : {}

 

Notice that PSGallery is not trusted but MSPSGallery is trusted.

If you want to see the modules available use Find-Module.

The first time you do expect this:

£> Find-Module

NuGet-anycpu.exe is required to continue.
PowerShellGet requires NuGet-anycpu.exe to interact with NuGet based galleries. NuGet-anycpu.exe must be available in
‘C:\Program Files\OneGet\ProviderAssemblies’ or ‘C:\Users\Richard\AppData\Local\OneGet\ProviderAssemblies’. For more
information about NuGet provider, see http://oneget.org/NuGet.html. Do you want PowerShellGet to download
NuGet-anycpu.exe now?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is “Y”):

You’re going to have to download the nuget file to continue.

On many modules I get this error:

Cannot convert value “18/12/2014 04:22:19 +00:00″ to type “System.DateTime”. Error: “String was not recognized as a valid DateTime.”
At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\PowerShellGet\PSGet.psm1:1402 char:49
+ …               PublishedDate = if($published){ [DateTime]$published };
+                                                   ~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvalidCastParseTargetInvocationWithFormatProvider

If you use:

Find-Module -ErrorAction SilentlyContinue

You will get a display like this:

Version  Name           Repository Description
——-   —-                ———- ———–
1.0.0      xFirefox       PSGallery  Firefox Main module
1.0.0      xChrome      PSGallery  Module for installing the chrome
0.2.16.2 xJea              PSGallery  Module with DSC Resources for  (JEA)

and many more.

The date related error isn’t consistent – for instance the Pester module:

£> Find-Module -Name Pester | fl *
Name                       : Pester
Version                    : 3.2.0
Description                : Pester provides a framework for running BDD style Tests to execute and validate PowerShell commands inside of PowerShell and offers a powerful set of Mocking Functions that allow tests to mimic and mock the functionality of any command inside of a piece of powershell code being tested. Pester tests can execute any command or script that is accesible to a pester test file. This can include functions, Cmdlets, Modules and scripts. Pester can be run in ad hoc style in a console or it can be integrated into the
Build scripts of a Continuous Integration system.
Author                     : Pester Team
CompanyName                :
Copyright                  : Copyright (c) 2014 by Pester Team, licensed under Apache 2.0 License.
PublishedDate              : 12/03/2014 17:38:11
LicenseUri                 : http://www.gnu.org/licenses/gpl-2.0.html
ProjectUri                 : https://github.com/Pester/Pester
IconUri                    : http://pesterbdd.com/images/Pester.png
Tags                       : {powershell, unit, testing, bdd…}
Includes                   : {Function, DscResource, Cmdlet, Command}
PowerShellGetFormatVersion :
ReleaseNotes               :
RequiredModules            :
RepositorySourceLocation   : https://www.powershellgallery.com/api/v2/
Repository                 : PSGallery
OneGetProvider             : NuGet

But looking at the Pester module in the PowerShell gallery – https://www.powershellgallery.com – it shows a date of 2014-12-03  i.e. 3 December 2014.  My system is interpreting that as 12 March 2014 because I’m using the English convention for dates in my culture settings.

£> $PSCulture
en-GB
£> $PSUICulture
en-US

Using

Set-Culture -CultureInfo en-US

Restarting PowerShell then running

£> $PSCulture
en-US
£> Find-Module

Stops the error messages so

Find-Module | select Name, Version, PublishedDate

works. Just remember your dates are in US format – Month/Day/Year

The last step is downloading and installing a module. The Install-Module cmdlet does that for us.

£> Get-Command Install-Module -Syntax

Install-Module [-Name] <string[]> [-MinimumVersion <version>] [-RequiredVersion <version>] [-Repository <string[]>]
[-Scope <string>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]

Install-Module [-InputObject] <psobject[]> [-Scope <string>] [-Force] [-WhatIf] [-Confirm] [<CommonParameters>]

Notice that there isn’t a choice of destination. You

£> Install-Module -Name Pester

You are installing the module ‘Pester’ from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet.
Are you sure you want to install software from ‘https://www.powershellgallery.com/api/v2/’?
[Y] Yes  [N] No  [S] Suspend  [?] Help (default is “Y”): y

This installs the module:

> Get-Module -ListAvailable p*
Directory: C:\Program Files\WindowsPowerShell\Modules
oduleType Version    Name                                ExportedCommands
——— ——-    —-                                —————-
cript     3.2.0      Pester                              {Describe, Context, It, Should…}

C:\Program Files\WindowsPowerShell\Modules was added to the modules path in PowerShell 4.0 and is the default location for DSC resources.

£> Install-Module -Name Pester –Force

Will override the message asking for confirmation.

December 17, 2014  10:00 AM

Selecting AD properties

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Active Directory, Powershell

Saw a question on the forums about selecting name properties using the Microsoft AD cmdlets.  By default Get-AdUser returns a limited subset of properties:

£> Get-ADUser -identity richard
DistinguishedName : CN=Richard,CN=Users,DC=Manticore,DC=org
Enabled           : True
GivenName         :
Name              : Richard
ObjectClass       : user
ObjectGUID        : 7c42be70-c6b2-401f-8296-46de9ee7446c
SamAccountName    : Richard
SID               : S-1-5-21-195014076-723736408-1406369008-1104
Surname           :
UserPrincipalName : Richard@Manticore.org

 

Given name = first name

 

if you want other properties you have to explicitly aske for them using the –Properties parameter. You can use a wildcard * but if you have a big AD that could be a lot of unrequired data you are pulling back. On the other hand if you want a lot of properties its often simpler to use the wildcard. As with most PowerShell related things there is no answer that is right all of the time.

 

The user asking the question wanted the first name, last name and department for all users in a given OU.  Use the OU as the –SearchBase.  The property you need to explicitly ask for is Department:

£> Get-ADUser -Filter *  -SearchBase ‘OU=Testing,DC=Manticore,DC=org’ -Properties Department | select GivenName, SurName, Department | fl *
GivenName  : Dave
SurName    : Green
Department : Testing

 

Selecting AD properties can be a little bit awkward if you forget that the default set is limited.  If in doubt of a property name – display them all for one user:

Get-ADUser -identity richard -Properties *


December 15, 2014  3:54 PM

WMI — identifying writable properties

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
CIM, Powershell, WMI

One common mistake I see is people trying to set the value of a read only property on a WMI class.  There isn’t a quick way to see if a property is writable. Get-CimClass can be used but you have to dig into the Qualifiers for each property.

 

You can use this function to determine the read\write settings on all of the properties of a WMI class

function get-cimreadwriteproperties {
[CmdletBinding()]
param (
[string]$classname
)

$props = @()

$class = Get-CimClass -ClassName $classname
$class.CimClassProperties |
foreach {
$prop = [ordered]@{
Name = $psitem.Name
Read = $false
Write = $false
}

$psitem |
select -ExpandProperty Qualifiers |
foreach {
if ($_.Name.ToLower() -eq ‘read’) {
$prop.Read = $true
}
if ($_.Name.ToLower() -eq ‘write’) {
$prop.Write = $true
}
}

$props += New-Object -TypeName PSObject -Property $prop
}

$props

}

 

Take the class name as a parameter and use Get-CimClass. Iterate through the properties and foreach create an output object. Test each qualifier to determine if read or write and set out to true. Add to array and output.

 

The output looks like this

 

£> get-cimreadwriteproperties -classname Win32_bios | ft -AutoSize

Name                  Read Write
—-                  —- —–
Caption               True False
Description           True False
InstallDate           True False
Name                  True False
Status                True False
BuildNumber           True False

etc

 

 

£> get-cimreadwriteproperties -classname Win32_LogicalDisk | ft -AutoSize

Name                          Read Write
—-                          —- —–
Caption                       True False
Description                   True False
InstallDate                   True False
<truncated>

ErrorMethodology              True False
NumberOfBlocks               False False
Purpose                       True False
<truncated>
VolumeDirty                   True False
VolumeName                    True  True
VolumeSerialNumber            True False


December 12, 2014  2:10 PM

Delivering PowerShell code

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Do you have a need to deliver PowerShell code to multiple machines. I do. I have a dev environment plus test and production environments. I need to move code from dev through test and production.

One way to do this is to create all of your code as modules and use the PowerShell 5.0 feature – PowerShellGet.

This feature enables you to install modules from a central repository. The default is a public, cloud based, repository but you can create your own.

I’m going to investigate the possibilities in a series of posts


December 10, 2014  1:40 PM

Learning PowerShell

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I’ve been thinking about how people learn PowerShell through watching some people at work who are learning it and watching the questions on the forums – many of which start off “I’m new to PowerShell and…”

There seems to be two broad approaches:

Option 1 is to get some training. This could be a formal course, video training or the excellent Learn PowerShell in a Month of Lunches book. Then find problems to solve.

Option 2 takes the opposite approach and finds the problems to solve then starts figuring out how to use PowerShell to solve them.

Which route you take depends on a number of things:

how you like to learn

the environment you work in – can you actually find time to start using PowerShell, and approval,

the technologies you work with

The options above both assume that scripting is your end goal. I’d suggest one other approach that may help. Use the cmdlets you have directly from the command line.  You can accomplish a lot by piping a few cmdlets together. This gets you used to working with cmdlets and the PowerShell language.

Keep it Simple and start small. You’ll soon start progressing.


December 9, 2014  2:54 PM

What Formatting cmdlets do to your data

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I have seen an increasing number of questions recently where the answer has been to remove Format-Table from the pipeline. As an example consider the names of the processes running on your machine

Get-Process -Name calc | Stop-Process

works because you are piping the selected object into the Stop-Process cmdlet.

Now think about this

Get-Process -Name calc | select Name | Stop-Process

a bit more long winded (the select isn’t necessary) but the resultant object hitting Stop-Process identifies a process by name which is all Stop-Process needs to work.

As a way to approach the reason for the first sentence in the post is there any difference between these two statements?

Get-Process -Name calc

Get-Process -Name calc | Format-Table

The output to screen looks identical. So  I should be able to do this:

Get-Process -Name calc | Format-Table | Stop-Process

What I get is a bunch of errors of the form:

Stop-Process : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:1 char:41
+ Get-Process -Name calc | Format-Table | Stop-Process
+                                         ~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (Microsoft.Power…FormatStartData:PSObject) [Stop-Process], ParameterB
indingException
+ FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.StopProcessCommand

The reason for this is simple.  The Format cmdlets destroy your pipeline and objects.  They take what they are given and output formatting directives. The only thing you can do with them is display them on screen (you can pipe to a text file but effectively the same thing)

If in doubt about what you’re dealing with at any time use Get-member

£> Get-Process | Get-Member

TypeName: System.Diagnostics.Process

£> Get-Process | select name | Get-Member

TypeName: Selected.System.Diagnostics.Process

Notice the slight change in that its now Selected.System.Diagnostics.Process instead of System.Diagnostics.Process

This only applies if you’re selecting a subset of properties. If you want the first N objects

£> Get-Process | select -First 5 | Get-Member

TypeName: System.Diagnostics.Process

You still have the original type.

However, lets look at formatting

£> Get-Process | Format-Table | Get-Member

TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.GroupEndData

TypeName: Microsoft.PowerShell.Commands.Internal.Format.FormatEndData

 

You get 5 different objects out – none of which have anything to do with your processes – apart from the values.

The Format cmdlets are designed to format your data for display. Thats all they do. Once your data hits the Format cmdlets that data can only be used fro display – nothing else. Put your Format cmdlets at the end of your pipeline and don’t attempt to do anything else with the data.


November 28, 2014  12:38 PM

Add a drop down to a Word document

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Microsoft Word 2013, Powershell

 

Its surprisingly easy to programatically add a drop down list to Word document

 

$Word = New-Object -Com Word.Application
$word.visible = $true
$template = “c:\test\template.docx”
$Doc = $Word.Documents.Open($template)
$Doc.Activate()

$cntrl = [Enum]::Parse([Microsoft.Office.Interop.Word.WdContentControlType], “wdContentControlDropdownList”)

$objCC = $doc.ContentControls.Add($cntrl)

$objCC.DropdownListEntries.Add(“PowerShell”)
$objCC.DropdownListEntries.Add(“Ruby”)
$objCC.DropdownListEntries.Add(“Perl”)

 

Create the COM object for Word and set visible.  Open a template (in this case a blank document) file and activate.

 

Set the type of control you want to add and define the possible entries.

 

It should be possible to set the default text but the method for doing that appears to be very awkward.  I’ll publish that if I work it out.


November 27, 2014  2:46 PM

The little changes that make a difference

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Each version of PowerShell introduces a new headline feature – remoting, workflows, DSC, OneGet in version 2,3,4 and 5 respectively. While this can change the way we work there are also a host of little changes that come along that are often overlooked.

One example is a change to Get-ChildItem introduced in PowerShell 3.0.

Consider getting a directory listing:

Get-ChildItem -Path C:\Windows

This will give all subfolders and file in the given folder.

If you just wanted the files you had to do this:

Get-ChildItem -Path C:\Windows | where {$_.PSIsContainer}

If you want just the files you use:
Get-ChildItem -Path C:\Windows | where {-not $_.PSIsContainer}

or the slightly shorter but not as easy to read:
Get-ChildItem -Path C:\Windows | where {!$_.PSIsContainer}

The PSIsContainer property name is not intuitive and I rarely remember the name exactly and try ISPSContainer first or some other variant.

Two additional filtering parameters were added to Get-ChildItem

Get-ChildItem -Path C:\Windows –Directory

and
Get-ChildItem -Path C:\Windows -File

produce listings of folders and files respectively.

A small simple change that makes life easier.

There are a lot of small changes like this scattered through the later PowerShell versions – I’d recommend going through the release notes to track down the ones that will be useful to you.


November 26, 2014  1:53 PM

Persisting PowerShell Objects

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I was asked recently about persisting PowerShell objects. The idea was to test a particular property on a semi-regular basis and save the object with the highest value for the property.  If the next test has a higher value it is saved and overwrites the existing object.

 

There are a number of ways to do this – if you are running the test very frequently then you could keep the object in memory as a variable. If you are testing fairly infrequently then you may want to persist the object to disk.  The CliXml cmdlets are good for this.

 

Start by creating a reference object:

Get-Process -Name powershell | Export-Clixml -Path proc1.xml

 

Then run some more tasks in PowerShell to increase the CPU usage. You can do this with any property that will change over time.  CPU is an easy example. You can then run your test. I used an instance of ISE to do the test so I didn’t alter the value by running the test.

 

$proc = Get-Process -Name powershell

Get-ChildItem -Path .\proc1.xml

$psp = Import-Clixml -Path .\proc1.xml

if ($proc.CPU -gt $psp.CPU){

$proc | Export-Clixml -Path proc1.xml

}

Get-ChildItem -Path .\proc1.xml

 

Get the current data and test the saved file. Import the saved file.  Test the CPU values and if the new value is higher save the object by overwriting the file.  Repeat as required.

 

The help files for Export-CliXml and Import-CliXml should be read.


November 25, 2014  2:17 PM

PowerShell Summit Europe 2015 – – topics to include

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

Are you planning on attending the PowerShell Summit Europe 2015.  If so what topics would you like to see covered. The Summit is deliberately pitched at a high level so you won’t find beginner level content.

If there’s a topic you’d like to be included in the agenda – leave a comment explaining what it is and why you think it would make a good Summit topic. No promises but we’ll see what we can do


Forgot Password

No problem! Submit your e-mail address below. We'll send you an e-mail containing your password.

Your password has been sent to: