PowerShell for Windows Admins


May 9, 2013  1:43 PM

Scripting Games–making work

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I saw this in one of the submissions:

$Properties = @{}
$Properties['Computer'] = $SystemInfo.__SERVER
$Properties['OperatingSystem'] = “$($OSInfo.Caption) – $($OSInfo.CSDVersion)”
$Properties['PhysicalMemory'] = $SystemInfo.TotalPhysicalMemory

My immediate thought was the entrant likes making work for themselves. The hash table can be created in a much simpler manner

$Properties = @{
Computer = $SystemInfo.__SERVER
OperatingSystem = “$($OSInfo.Caption) – $($OSInfo.CSDVersion)”
PhysicalMemory = $SystemInfo.TotalPhysicalMemory
}

Same result. Less typing and easier to read when you come back to the script in 6 months time

May 8, 2013  1:08 PM

AD MoL Chapter 10 MEAP

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Chapter 10 of AD Management in a Month of Lunches is now available.

http://www.manning.com/siddaway3/

The chapter covers Fine Grained Password Policies


May 7, 2013  3:21 PM

Scripting Games–new Get-ChildItem parameters

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

One improvement that came with PowerShell v3 is the –File and –Directory parameters on Get-ChildItem

If I run this

Get-ChildItem -Path c:\mydata

I will get a mixture of directories and files

Mode LastWriteTime Length Name
—- ————- —— —-
d—- 19/11/2012 20:19 Delivery
d—- 26/02/2013 19:24 Demo
d—- 05/05/2013 11:26 ScriptingGames 2013
d-r– 07/05/2013 18:17 SkyDrive
d—- 24/01/2013 20:08 Summit NA 2012
-a— 06/05/2013 15:26 1336320 2013May_ErrorHandling.doc

If I add the –Recurse parameter it gets worse – I know I’ve got a lot of files and directories in here.

In PowerShell v2 you could separate the directories and files by using PSISContainer

Get-ChildItem -Path c:\mydata | where {$_.PSIsContainer}
Get-ChildItem -Path c:\mydata | where {!$_.PSIsContainer}

will give you the directories only and files only respectively.

It gets easier in PowerShell v3

Get-ChildItem -Path c:\mydata -Directory
Get-ChildItem -Path c:\mydata –File

simple and obvious when you read it.

if you are using PowerShell v3 don’t forget these parameters


May 7, 2013  3:00 PM

Scripting Games-don’t repeat the work

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

There are some good features to this script but what really hurts is the two trips to the server for the Win32_Computersystem class

Foreach ($IP in (Get-Content “C:\IPList.txt”))
{
$Name = (Get-WMIObject Win32_ComputerSystem -ComputerName $ip).Name
$Mem = [math]::truncate((Get-WMIObject Win32_ComputerSystem -ComputerName $ip).TotalPhysicalMemory / 1MB)
$Ver = (Get-WMIObject Win32_OperatingSystem -ComputerName $ip).Caption
[Array]$Cpus = (Get-WMIObject Win32_Processor -ComputerName $ip)
$CpuCount = $Cpus.Count
$Cores = 0
Foreach ($Socket in $Cpus)
{
$Cores = $Cores + $Socket.NumberOfCores
}
“$Name,$Mem,$Ver,$CpuCount,$Cores” | Out-File output.csv -Append
}
it would be better to do this
$comp = Get-WMIObject Win32_ComputerSystem -ComputerName $ip
$name = $comp.Name
$mem = [math]::truncate($comp.TotalPhysicalMemory / 1MB)

Dropping one round trip on a few servers isn’t that big a deal. dropping it on 3000 servers will make a difference
Always think about how your scripts may need to scale one day


May 7, 2013  2:15 PM

Scripting Games–how not to output data

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I haven’t finished blogging about event 1 yet but this caught my eye.

Things aren’t too bad until we hit the bunch of write-host calls

$wrks = (Get-Content -path C:\IPList.txt)
foreach ($wrk in $wrks)
{
$osver = Get-WMIObject -class win32_operatingsystem -ComputerName $wrk
$procs = @(Get-WMIObject -class win32_processor -ComputerName $wrk)
$psok=($procs.SocketDesignation).count
$pcors=(($procs.numberofcores[0])*$psok)
$plog=($pcors * 2)
$psped=$procs.MaxClockSpeed[0]
$mem = Get-WMIObject -class win32_physicalmemory -ComputerName $wrk
$memtotal = ($mem | Measure-Object -Property capacity -Sum)
$memgb = $memtotal.sum/1gb
Write-host “*******************************************************”
Write-Host “Machine Name: ” $osver.CSName
Write-Host “OS: “$osver.caption
Write-Host “Service Pack: “$osver.csdversion
Write-Host “Build #: “$osver.version
Write-Host “*********** ”
Write-Host “Memory Installed:”
Write-Host “*********** ”
Write-Host “Memory (GB): $memgb ”
Write-Host “Slots used:” $memtotal.Count
Write-Host “*********** ”
Write-Host “Processor(s) Installed:”
Write-Host “*********** ”
Write-Host “Sockets:” $psok
Write-Host “Cores:” $pcors
Write-Host “Logical Procs:” $plog
Write-Host “*********** ”
Write-Host “Processor Details:”
Write-Host “*********** ”
$procs
Write-Host “”
}

The correct way is to create an object and output that

I’ll be blogging a sample answer when the games are over. for now be aware that write-host is worse than backticks


May 6, 2013  11:53 AM

Scripting Games: event 1–use of robocopy

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

The object of the exercise in both the beginners and advanced sections of event 1 was to move a set of log files older than a give data to an archive folder.

A number of solutions were presented that used robocopy.

This is a workable solution that meets the lettter of the objective but it doesn’t really meet the spirit of the games.

The Scripting Games is about learning to use PowerShell. Within PowerShell there is a move-item cmdlet.

Robocopy isn’t required. Using non-Powershell tools can often be harder than using PowerShell. In your work, and especially in the games, think very carefully before reaching for a non-PowerShell command


May 6, 2013  9:27 AM

Scripting Games–major dislike #2

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I’ve already blogged about incorrect use of backticks. Here is another example of un-necessary use of backticks

$Files= Get-ChildItem `
-Path $Path `
-include $Type `
-Recurse `
-File |
Where-Object {$_.LastWriteTime -lt (get-date).AddDays(-$OlderThan) }

Spreading out one parameter per line like this doesn’t add anything to the script and makes working on the get-childitem cmdlet code harder if you want to change it.

Don’t do this


May 6, 2013  7:53 AM

Scripting games-major dislike

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

One of the things we were asked to blog about as Scripting Games judges was things we liked and disliked. This code is a major dislike

Get-ChildItem $sourceDirectory | ? {$_.PsISContainer } |
% { $subDirectory = $_ ; Get-ChildItem (“$sourceDirectory\$subDirectory”) -Include *.LOG -Recurse } |
? { $logFile = $_ ; $logFile.LastWriteTime -le $modifiedCutOffDate } |
% { $logFileAndSubDirDictionary.Add($logFile, $subDirectory) }

Two things really make this stand out as how not to do things:

Using % & ? as aliases in a script. They are tolerable (just) in an interactive command but have no place in a script. Tab completion is so easy. Use the proper command.
Putting multiple commands on a line separated by ; It makes the code hard to read and awkward to work out whats going on. It also makes script testing, debug and maintenance much more difficult
Avoid these two things in your scripts


May 6, 2013  4:20 AM

Scripting Games–integer parameters

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I keep seeing parameter constructs like this:

[int]$age = ’90′

Why set the parameter to an integer and then set the default as a string. PowerShell will convert but it just doesn’t make sense.

All you need is

[int]$age = 90


May 5, 2013  4:30 PM

Scripting games–using parameters

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I am seeing an incredible number of scripts that have this sort of coding round parameters

# Input from the user
[Parameter(Mandatory=$false,
ValueFromPipeline=$False,
Position=0)]
[ValidateScript({Test-Path $_ })]
[String]$SourcePath = ‘C:\Application\Log’,

[Parameter(Mandatory=$false,
ValueFromPipeline=$False,
Position=1)]
[ValidateScript({Test-Path $_ })]
[String]$ArchivePath = ‘\\NASServer\Archives’,

[Parameter(Mandatory=$false,
ValueFromPipeline=$False,
Position=2)]
[Int]$Days = 90

Why do you need to state that Mandatory=$false or that ValueFromPipeline=$False. The DEFAULT values are false. You only need to use them if you are setting them to TRUE.

Its a waste of coding time and processing time when you run the script.

I remember blogging about this last year.

Please stop doing it so I don’t have to blog about it next year


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: