PowerShell for Windows Admins


September 27, 2015  1:04 PM

Where next?

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

With the demise of the European PowerShell Summit organised by powershell.org what should happen next in Europe as far as a PowerShell conferences are concerned?

The probability is that there will be a number of regional conferences. Tobias already has a German language conference and people at the Stockholm Summit were talking about an event based in Scandinavia.

What about the UK?

I been talking to a number people and ideas are beginning to coalesce.

Suggestions so far include a one day PowerShell Saturday and a PowerShell conference modelled on the recent Summits.

What do you want to see?

Its no use putting events together if its not what the UK PowerShell community actually wants.

Leave a comment if you have views for or against either of the two ideas and if you have another option please let me know

September 27, 2015  11:47 AM

Bad habits when creating objects

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

I’m seeing a lot of code recently that looks like this

$comp = Get-CimInstance -ClassName Win32_ComputerSystem
$os = Get-CimInstance -ClassName Win32_OperatingSystem

$obj  = New-Object -TypeName PSObject

$obj | Add-Member -MemberType NoteProperty -Name Name -Value $comp.Name
$obj | Add-Member -MemberType NoteProperty -Name  Manufacturer -Value $comp.Manufacturer
$obj | Add-Member -MemberType NoteProperty -Name Model -Value $comp.Model
$obj | Add-Member -MemberType NoteProperty -Name OS -Value $os.Caption
$obj | Add-Member -MemberType NoteProperty -Name SP -Value $os.ServicePackMajorVersion
$obj | Add-Member -MemberType NoteProperty -Name Architecture -Value $os.OSArchitecture

$obj

A couple of calls to CIM classes then a long laborious way to create an output object.  Way, way, way back in the day you had to create objects like this. Life and PowerShell have moved on.

There are much simpler ways to get the job done that involve far less typing. The more you type the greater the chance of introducing an error. Debugging a mass of Add-member calls is not something you ever want to do.

So easy way number 1.

$comp = Get-CimInstance -ClassName Win32_ComputerSystem
$os = Get-CimInstance -ClassName Win32_OperatingSystem

$props = @{
‘Name’ = $comp.Name
‘Manufacturer’ = $comp.Manufacturer
‘Model’ = $comp.Model
‘OS’ = $os.Caption
‘SP’ = $os.ServicePackMajorVersion
‘Architecture’ = $os.OSArchitecture

}
$obj  = New-Object -TypeName PSObject -Property $props

$obj

Create a hash table of the properties you want your object to have and pass that to New-Object. I prefer to create the hash table as a separate step as I find it easier to read.

This is far, far quicker than multiple calls to Add-Member. Its also safer as there’s a lot less to type.

You can make the process a bit simple – easy way number 2:

$comp = Get-CimInstance -ClassName Win32_ComputerSystem
$os = Get-CimInstance -ClassName Win32_OperatingSystem

$obj = [PSCustomObject]@{
‘Name’ = $comp.Name
‘Manufacturer’ = $comp.Manufacturer
‘Model’ = $comp.Model
‘OS’ = $os.Caption
‘SP’ = $os.ServicePackMajorVersion
‘Architecture’ = $os.OSArchitecture

}

$obj

Create the object directly from the hash table – note that you have to make the type PSCustomObject not PSObject.

There’s no excuse for creating objects with multiple calls to Add-Member


September 9, 2015  12:58 PM

Scripting Games–August 2015

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The August 2015 Scripting Games puzzle was a simple ‘get data from a web service’ request. You had to retrieve data from a given web service

£> Invoke-RestMethod -Uri ‘http://www.telize.com/geoip’ |
Select-Object -Property  longitude, latitude, continent_code, timezone
longitude latitude continent_code timezone
——— ——– ————– ——–
-0.13     51.5 EU             Europe/London

Apart from the inaccuracy in the continent information – England isn’t part of Europe Smile – its pretty straight forward. The trick is remembering that Invoke-RestMethod exists and selecting out the properties you need.

If you wanted to productionise this you end up with the follwoing function as a minimum

function Get-GeoInformation
{
[CmdletBinding(SupportsShouldProcess=$true)]
param (
[Parameter(Position=0, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[Alias(‘ip’, ‘address’)]
[ValidateScript({([System.Net.IPAddress]::TryParse($_, [ref]’127.0.0.1′)) -eq $true})]
[string[]]$ipaddress
)

BEGIN {} #end begin
PROCESS {
try {
if ($ipaddress) {
foreach ($ip in $ipaddress){
if ($psCmdlet.ShouldProcess(“$ip”, ‘Retreiving geo-location data’)) {
Write-Verbose -Message “Retrieving data for $ip”
Invoke-RestMethod -Method GET -Uri “http://www.telize.com/geoip/$ip” -ErrorAction Stop
}
} # end foreach ($ip in $ipaddress)
} # end if ($ipaddress)
else {
Write-Verbose -Message ‘Retrieving data’
if ($psCmdlet.ShouldProcess(‘default ip address’, ‘Retreiving geo-location data’)) {
Invoke-RestMethod -Uri ‘http://www.telize.com/geoip’-ErrorAction Stop
}
} #end else
} # end try
catch [System.Net.WebCmdletWebResponseException] {
throw ‘Error connecting to web service’
}
catch {
throw ‘Unresolved error’

} # end catch

} #end process
END {} # end end

<#
.SYNOPSIS
Get-GeoInformation returns data from the geoip web service
at http://www.telize.com/

.DESCRIPTION
Get-GeoInformation returns data from the geoip web service
at http://www.telize.com/

If an IP address is presented to the function that will be sent to
the web service otherwise the current public IP address of the user,
or their ISP, is used.

.PARAMETER  ipaddress
IP address to be tested. The IP address will be validated on input
and an error thrown if invalid

.EXAMPLE
£> Get-GeoInformation
longitude      : -0.13
latitude       : 51.5
asn            : AS9105
offset         : 1
ip             : 88.108.92.247
area_code      : 0
continent_code : EU
dma_code       : 0
timezone       : Europe/London
country_code   : GB
isp            : Tiscali UK
country        : United Kingdom
country_code3  : GBR
No IP address is presented so the user’s public (ISP address) is used

.EXAMPLE
£> Get-GeoInformation -ipaddress 88.108.92.247
longitude      : -0.13
latitude       : 51.5
asn            : AS9105
offset         : 1
ip             : 88.108.92.247
area_code      : 0
continent_code : EU
dma_code       : 0
timezone       : Europe/London
country_code   : GB
isp            : Tiscali UK
country        : United Kingdom
country_code3  : GBR

An IP address is used

.NOTES
Written in response to the August 2015 Scripting Games puzzle
from PowerShell.org

.LINK
http://www.telize.com
http://powershell.org/wp/2015/08/01/august-2015-scripting-games-puzzle/
#>

}

The function takes one, or more, IP addresses as an optional parameter and after testing its a valid IP address proceeds to call the web service for each IP address.

The –Confirm and –WhatIf parameters are available on the function via the  CmdletBinding:

[CmdletBinding(SupportsShouldProcess=$true)]

I’ve also added comment based help.

Its interesting that a single PowerShell pipeline goes to over 100 lines of code when you add in the validation, error handling and other aspects of producing production code


September 8, 2015  4:37 AM

WMF 5.0 Production Preview

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The PowerShell team recently announced the availability of the WMF 5.0 Production Preview. This is available for:

Windows Server 2012 R2
Windows Server 2012
Windows 2008 R2 SP1
Windows 8.1
Windows 7 SP1

You’ll need .NET Framework 4.5 installed to run WMF 5.0

see – http://blogs.msdn.com/b/powershell/archive/2015/08/31/windows-management-framework-5-0-production-preview-is-now-available.aspx

NOTE  WMF 5.0 PP will NOT load on Windows 10 – as WMF 5.0 PP is effectively the version on Windows 10 plus some bug fixes.

Windows 10 will have an update mechanism when WMF 5.0 is RTM – see http://blogs.msdn.com/b/powershell/archive/2015/08/06/windows-management-framework-wmf-5-0-roadmap.aspx


August 30, 2015  10:50 AM

Manning Deal of the Day–31 August 2015

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

Deal of the Day August 31: Half off my book Learn Active Directory Management in a Month of Lunches. Use code dotd083115au at https://manning.com/books/learn-active-directory-management-in-a-month-of-lunches

Learn ConfigMgr 2012 in a Month of Lunches and Learn SQL Server Administration in a Month of Lunches also included

 


August 28, 2015  1:31 PM

PowerShell User Group meeting in Manchester

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The UK PowerShell User Group was the World’s first user group dedicated to PowerShell. Its been in hibernation for the last few years but Ryan Yates is bringing it back to life.

He’s set up a meeting in Manchester for Tuesday 13 October 2016. See  http://www.kilasuit.org/Blog/Post/18/Coming-to-Manchester-October-13th—Get-PSUGUKMan

for details.

If you’re in the area I’d recommend you get there if you can


August 27, 2015  12:38 PM

PowerShell Summit Europe 2015 final schedule

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

The schedule for the PowerShell Summit Europe 2015 has been finalised.  See the event site at http://eventmgr.azurewebsites.net/event/home/PSEU15#schedule


August 26, 2015  2:33 PM

PowerShell gallery security

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

There are a number of concerns with pulling code from the Internet e.g. code quality and whether it contains malicious actions.

The PowerShell gallery has implemented a couple of features that should help. All modules have to meet a minimum quality standard as outlined here – http://blogs.msdn.com/b/powershell/archive/2015/08/07/powershell-gallery-new-security-scan.aspx

You can also use Save-Module to download the module to a safe location so you can inspect the contents.


August 19, 2015  10:03 AM

Creating JSON from a hash table

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
JSON, Powershell

A question on the forum was asking about creating a JSON structure for use with a REST API.

The original has table looked like this

$body = @{
auth_token = “$auth_token”
items = @{
label = “server1”
value = “OK”
label = “server2”
value = “OK”
}
}

This fails because you can’t have duplicate keys in a hash table.

One way to create the JSON is like this

$items = @()
$items += @{label = “server1”; value = “OK”}
$items += @{label = “server2”; value = “OK”}

$body = New-Object -TypeName PSOBject -Property @{
auth_token = “$auth_token”
Items = $items
}

ConvertTo-Json -InputObject $body

which gives:

{
“Items”:  [
{
“value”:  “OK”,
“label”:  “server1”
},
{
“value”:  “OK”,
“label”:  “server2”
}
],
“auth_token”:  “”
}

The items are created by adding hah tables to a pre-existing hash table.  You’re creating a hashtable of hash tables.

If you need to control the order of values you need an ordered hash table to use with New-Object

$items = @()
$items += @{label = “server1”; value = “OK”}
$items += @{label = “server2”; value = “OK”}

$props =  [ordered]@{
auth_token = “$auth_token”
Items = $items
}

$body = New-Object -TypeName PSOBject -Property $props

ConvertTo-Json -InputObject $body

which gives

{
“auth_token”:  “”,
“Items”:  [
{
“value”:  “OK”,
“label”:  “server1”
},
{
“value”:  “OK”,
“label”:  “server2”
}
]
}

 

 


August 18, 2015  7:33 AM

Passing data into Jobs

Richard Siddaway Richard Siddaway Profile: Richard Siddaway
Powershell

PowerShell jobs are a powerful tool for running processes in the background. You have to remember that a PowerShell job runs in a separate process that only exists for the duration of the job. The results are returned to your process when the job finishes.

A user on the forum asked about passing variables into jobs.  He’d done this:

$w = ‘1..10’
Invoke-Expression $w

and got the expected result

1
2
3
4
5
6
7
8
9
10

He then tried this

Start-Job -ScriptBlock { Invoke-Expression $w}

But the job said

Cannot bind argument to parameter ‘Command’ because it is null.
+ CategoryInfo          : InvalidData: (:) [Invoke-Expression], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.InvokeExpre
ssionCommand
+ PSComputerName        : localhost

Irrespective of whether using Invoke-Expression is a good idea  and I’ll cover that in a future post the job fails because its failing to find $w because the job is running in a separate PowerShell process and $w doesn’t exist.

The simplest answer is to make the job self contained

Start-Job -ScriptBlock { $w = ‘1..10’; Invoke-Expression $w}

Everything is in the script block passed to the job and therefore passed to the new PowerShell process.

Alternatively, you can pass the variable $w into the script block

Start-Job -ScriptBlock { param($w) Invoke-Expression $w} -ArgumentList $w

You need to define a param block on the script block and pass the $w variable to it using the –Argumentlist parameter on start-job.

Another option introduced with PowerShell 3.0 us the $using scope modifier

Start-Job -ScriptBlock { Invoke-Expression $using:w}

Which is another way to say use the $w variable from the current scope

You can find out more on $using from about_scopes, about_Remote_Variables


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: