May 2011 - Posts

PowerShell Basics: 3 When not to use PowerShell

Is there a time when you shouldn’t use PowerShell – No

Is there a time when there might be better alternatives – Yes

I can think of a number of situations where writing a PowerShell script may not be the most effective answer:

  • a simple one off or very infrequent task that can be done in the GUI
  • you have a VBScript that works – convert it to PowerShell when it needs updating not now
  • there is a command line utility that can do the job that you know how to use
  • you need maximum performance – means you need compiled code

There will be more that you can think of but these cover the main scenarios I’ve seen.

Spend your PowerShell time being productive and automate the most common and/or repetitive tasks first. You’ll soon save the time to tackle the more difficult, complex or infrequent tasks

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell Basics: 2 Utility cmdlets

 

The utility cmdlets are the glue that we use to bind our actions together on the PowerShell pipeline. We can easily discover the list of utility cmdlets by using

Get-Command *-object

I split the utility cmdlets into two groups.  The first group I use all of the time

ForEach-Object
Select-Object
Sort-Object
Where-Object

The second group provides functionality that I don’t use as much but saves me a ton of time when I need it

Compare-Object
Group-Object
Measure-Object
New-Object
Tee-Object

PowerShell’s verb-noun naming convention for cmdlets should be sufficient to figure out what each of them does.  Check out the help file for these cmdlets.  Another good reference is the web cast I did a few months back

Details from here http://msmvps.com/blogs/richardsiddaway/archive/2011/02/08/tonight-s-slides-and-recording.aspx

Advanced Function Template typo

Just discovered a typo in the advanced function template I posted.

http://msmvps.com/blogs/richardsiddaway/archive/2011/05/24/advanced-function-template.aspx

It should be

function aaaa-yyyyyy{
[CmdletBinding(SupportsShouldProcess=$true,
    ConfirmImpact="Medium Low High None",
    DefaultParameterSetName="XXXXX")]
param (
[parameter(Position=0,
   Mandatory=$true,
   ParameterSetName="YYYYYYYYYY",
   ValueFromPipeline=$true,
   ValueFromPipelineByPropertyName=$true,
   ValueFromRemainingArguments=$true,
   HelpMessage="Put your message here" )]
   [Alias("CN", "ComputerName")] 
   [AllowNull()]
   [AllowEmptyString()]
   [AllowEmptyCollection()]
   [ValidateCount(1,10)]
   [ValidateLength(1,10)]
   [ValidatePattern("[A-Z]{2,8}[0-9][0-9]")]
   [ValidateRange(0,10)]
   [ValidateScript({$_ -ge (get-date)})]
   [ValidateSet("Low", "Average", "High")]
   [ValidateNotNull()]
   [ValidateNotNullOrEmpty()]
   [string]$computer="."
)
BEGIN{}#begin
PROCESS{

if ($psCmdlet.ShouldProcess("## object ##", "## message ##")) {
    ## action goes here
}

}#process
END{}#end

<#
.SYNOPSIS


.DESCRIPTION


.PARAMETER  <Parameter-Name>


.EXAMPLE


.INPUTS


.OUTPUTS


.NOTES


.LINK

#>

}

 

The difference is in the Alias parameter.  It should be stand alone NOT included as part of the Parameter arguments.

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell UG–June 2011 reminder

Don’t forget to add the June meeting of the UK PowerShell User group to your calendar.  Its June 21 at 7.30pm BST. Details available from

http://msmvps.com/blogs/richardsiddaway/archive/2011/05/10/powershell-uk-user-group-june-meeting.aspx

Posted by RichardSiddaway | with no comments

Hardware Update

Last year I bought a Lenovo W510 to use as my mobile(ish) Hyper-V lab.  I mentioned the initial problems I’d had with core parking - http://msmvps.com/blogs/richardsiddaway/archive/2010/08/06/lenovo-w510-hyper-v-and-bsod.aspx

Once those were resolved I haven’t had any other problems with it.  I never did get Virtual Machine Manager installed – I tripped over a problem that VMM won’t recognise the agent. Other people have reported the same problem (with different hardware and configurations so its not a W510 issue) but there doesn’t seem to be a known fix.  I might try again with VMM some day but at the moment I can live without.

All in all very satisfied with the W510 – it does what I required and gives me sufficient performance for  experimentation and demos.

Last years other purchase was a HP Mini netbook

http://msmvps.com/blogs/richardsiddaway/archive/2010/07/11/netbook-revisited.aspx

This has been even better.  Its my travelling machine of choice. I can work with Office documents, browse Internet, use my e-readers and even write PowerShell scripts.  I’ve run a number of presentations off it and it works excellently.

Another winner.

Next purchase is likely to be a tablet of some kind. Maybe this year or maybe wait until next version of Windows and see what comes along then

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell Basics: 1 What is PowerShell?

Discussions at the PowerShell Deep Dive and the entries for the Scripting Games showed me that there is tremendous amount of interest in PowerShell but that there are a lot of people that are still unsure of how to get started on want to take PowerShell a bit further than stringing cmdlets together on the command line.

Don’t misunderstand me – the interactive use of PowerShell on the command line is one of its greatest strengths and something I use practically every day. In this series of posts I want to look at first steps in using the PowerShell language. We’ll cover using the pipeline as we go.

I’ve been asked many times what is Powershell and I’ve heard many answers to the question:

  • Interactive and interpreted .NET language
  • .NET at the command line
  • Scripting language
  • Command line admin tool
  • Command line admin tool for AD, Exchange, <insert your technology here>
  • Replacement for cmd.exe
  • Replacement for VBScript
  • etc
  • etc

My answer is that PowerShell is Microsoft’s Automation Engine. The greatest feature is that it can be used interactively and in scripts and you will get the same results. This means you can experiment at the command line, get everything working and then move it straight into a script. The opposite is true – you can run a script so that variables are kept in memory and then experiment to get round a difficult problem.

PowerShell can be thought of as two sets of functionality:

  • cmdlets that supply a way to do something
  • a language that wraps round the cmdlets to help manipulate the results.

In some cases the two can overlap  for instance if you came from a traditional scripting background you might do something like this

001
002
003
004
005
006

$processes = Get-Process 
foreach ($process in $processes
){
 
if ($process.Handles -gt 500
){
  
Write-Output $process
 }
}

 

Use the Get-Process to get a list of processes; test each process to see it uses more than 500 handles and if it does write out the process information.

At this point anyone who has used PowerShell will be shouting “use the pipeline”

We can get exactly the same results like this

Get-Process | Where-object {$_.Handles -gt 500}

 

We pipe the Get-Process results into Where-Object which filters out those processes with more than 500 handles.  We can then take it a stage further and sort the results

Get-Process | Where-object {$_.Handles -gt 500} | sort-object Handles –Descending

 

Adding a sort to our first way of doing things would be more difficult.

 

The first thing to remember about PowerShell is Get the pipeline to do the work for you. The PowerShell team have done an excellent job of supplying utility cmdlets such as sort that can save masses of time and effort compared to trying to write your own.

Next time we’ll look at the utility cmdlets in more depth.

Manning Deal of the Day–29 May

Today, for one day only, get 50% off PowerShell and WMI MEAP or MEAP+ebook.

 

The code is dotd0529cc when you order from www.manning.com

 

The same code can be used for PowerShell in Action – second edition and PowerShell in Practice

root\wmi–MSBatteryClass

A number of the classes in root\wmi return results from more than one class. That sounds odd but it can be explained by an example.

The namespace contains a number of classes related to the battery in laptops

gwmi -Namespace root\wmi -List *battery*

MSBatteryClassEvent
BatteryStatusChange
BatteryTagChange
MSBatteryClass
BatteryStaticData
BatteryRuntime
BatteryCycleCount
BatteryTemperature
BatteryStatus
BatteryFullChargedCapacity

 

We’ll ignore the event and change classes for now.  If we pick out the MSBattery class we get information from a number of other classes returned – MSBattery is a super class.

PS> gwmi -Namespace root\wmi -Class MSBatteryClass | select __class

__CLASS
-------
BatteryCycleCount
BatteryFullChargedCapacity
BatteryStaticData
BatteryRuntime
BatteryStatus

 

Be aware that the runtime property returned by BatteryRuntime doesn’t respond to calculations you may find on the Internet when your OS is Windows 7

Battery status is a useful class to determine if you are on battery or external power & if the battery is charging

Posted by RichardSiddaway | with no comments
Filed under:

root\wmi – speeding the testing

Testing the individual classes in root\wmi is a pain – so its time for some brute force.  I’ll select a group of classes and test the selection

gwmi -Namespace root\wmi -List system* | fw

 

I can then iterate through them calling get-wmiobject.

This is a command line activity so aliases are OK

gwmi -Namespace root\wmi -List system* | foreach {gwmi -Namespace root\wmi -Class $_.Name}

 

I could do this for all classes but it could become a bit difficult sorting out results if I get a lot of them.  We’ll see where this goes.

Posted by RichardSiddaway | with no comments
Filed under:

root\wmi – MS_SystemInformation

Continuing our exploration of the murky jungle that is the root\wmi namespace we find a number of classes related to system configuration

gwmi -Namespace root\wmi -List *system* | fw

 

Of theses the only one I could get a respnse from on Windows 7 or Windows 2008 R2 was MS_SystemInfo.

gwmi -Namespace root\wmi -Class MS_SystemInformation

Active                 : True
BaseBoardManufacturer  : Wistron
BaseBoardProduct       : 303C
BaseBoardVersion       : 08.48
BiosMajorRelease       : 15
BiosMinorRelease       : 52
BIOSReleaseDate        : 12/23/2008
BIOSVendor             : Hewlett-Packard
BIOSVersion            : F.34
ECFirmwareMajorRelease : 255
ECFirmwareMinorRelease : 255
InstanceName           : Root\mssmbios\0000_0
SystemFamily           : 103C_5335KV
SystemManufacturer     : Hewlett-Packard
SystemProductName      : HP G60 Notebook PC
SystemSKU              : NF300EA#ABU
SystemVersion          : F.34

 

It seems to combine some information from

Win32_ComputerSystem

Win32_BaseBoard

Win32_Bios

Posted by RichardSiddaway | with no comments
Filed under:

root\wmi - ProcessorBiosInfo

The root\wmi namespace contains a massive number of classes. Unfortunately there appears to be very little documentation available for these classes. Another issue is that many of classes do not appear to return anything (at least on my Windows 7 system – further testing is required).

In this smalls series I intend to dig through some of these classes and see if we can find anything useful

There are a number of classes associated with the processor:

PS> Get-WmiObject -Namespace 'root\wmi' -List *Processor* | fw


ProcessorCStateEvent                ProcessorPerfStateEvent
ProcessorThrottleStateEvent         ProcessorAcpiCsdDependency
MSProcessorClass                    ProcessorBiosInfo
ProcessorBiosTStates                ProcessorStatus
ProcessorAcpiCsd                    ProcessorAcpiTsd
ProcessorBiosCStates                ProcessorAcpiCst
ProcessorAcpiXpss                   ProcessorAcpiTssState
ProcessorAcpiCstState               ProcessorAcpiTsdDependency
ProcessorPerformance                ProcessorAcpiXpssState
ProcessorAcpiTss

 

Out of this list the ProcessorBiosInfo works on Windows 7

PS> Get-WmiObject -Namespace 'root\wmi' -Class ProcessorBiosInfo


Active           : True
ApicId           : 0
InstanceName     : ACPI\AuthenticAMD_-_x86_Family_17_Model_3_-_AMD_Athlon_Dual-Core_QL-62\_0_0
NtNumber         : 0
PBlk             : 4112
PBlkLen          : 6
Pct              : System.Management.ManagementBaseObject
ProcessorId      : 0
Pss              : System.Management.ManagementBaseObject

 

Active and Instance name are obvious. The NtNumber is the OS processor Id

PBlk refers to the processor control block – address and length

 

The Pct and Pss objects can be drilled down into

 

$cpu = Get-WmiObject -Namespace 'root\wmi' -Class ProcessorBiosInfo
$cpu[0]
$cpu[0].Pct
$cpu[0].Pct.Control
$cpu[0].Pct.Status
$cpu[0].Pss
$cpu[0].Pss.State

Follow these through to see what may be useful.

We can see everything like this

001
002
003
004
005
006
007
008
009
010

Get-WmiObject -Namespace 'root\wmi' -Class ProcessorBiosInfo |
foreach {
 $_ | select * -ExcludeProperty __*
 
 $_.Pct.Control | select * -ExcludeProperty __*
 $_.Pct.Status | select * -ExcludeProperty __*
 
 $_.Pss.State | select * -ExcludeProperty __*

}

 

The class description just returns the following

ACPI Bios Processor Information

Event log cleanup

If we look at the event logs

 

PS> Get-EventLog -List

  Max(K) Retain OverflowAction        Entries Log
  ------ ------ --------------        ------- ---
  20,480      0 OverwriteAsNeeded      19,975 Application
  20,480      0 OverwriteAsNeeded           0 HardwareEvents
     512      7 OverwriteOlder              0 Internet Explorer
  20,480      0 OverwriteAsNeeded           0 Key Management Service
   8,192      0 OverwriteAsNeeded           0 Media Center
     128      0 OverwriteAsNeeded       1,033 OAlerts
  20,480      0 OverwriteAsNeeded           0 Scripts
                                              Security
  20,480      0 OverwriteAsNeeded      56,535 System
  15,360      0 OverwriteAsNeeded      18,907 Windows PowerShell

 

If we look at a single log from this list in detail

PS> Get-EventLog -List | select -f 1 | fl *


Entries              : {RSLAPTOP01, RSLAPTOP01, RSLAPTOP01, RSLAPTOP01...}
LogDisplayName       : Application
Log                  : Application
MachineName          : .
MaximumKilobytes     : 20480
OverflowAction       : OverwriteAsNeeded
MinimumRetentionDays : 0
EnableRaisingEvents  : False
SynchronizingObject  :
Source               :
Site                 :
Container            :

 

So in the first display we get the number of entries but in the second we get the list of entries.  I want to be able to clean up my logs with lots of entries.

The difference between the two displays is due to the formatting engine in PowerShell. It is creating the entries count column. We know that the .NET object we are interested in is System.Diagnostics.EventLog so we can search for it.

Select-String -Path $pshome\*.ps1xml -Pattern "System.Diagnostics.EventLog" –SimpleMatch

 

The result comes back that we want to look at DotNetTypes.format.ps1xml.

Open that file – BE VERY CAREFUL THAT YOU DON’T ALTER THIS FILE OR BAD THINGS WILL HAPPEN. The sun will go nova, the earth will plunge into a black hole and oh yeah – even worse PowerShell will stop working.

If we search for System.Diagnostics.EventLog and look at the formatting options we will eventually find an entry that matches the first table above.  The Entries property is found as $_.Entries.Count.ToString('N0')

This means I can do this

Get-EventLog -List | where {$_.Entries.Count -gt 10000}


Max(K) Retain OverflowAction        Entries Log
------ ------ --------------        ------- ---
20,480      0 OverwriteAsNeeded      19,976 Application
20,480      0 OverwriteAsNeeded      56,543 System
15,360      0 OverwriteAsNeeded      18,907 Windows PowerShell

 

I can now do my clean up

Get-EventLog -List | where {$_.Entries.Count -gt 10000} | foreach {Clear-EventLog -LogName $_.Log}

 

but you have to be running PowerShell with elevated privileges.

 

Remembering how the formatting system works and how some of the displays are formatted can save a lot of effort sometimes.

Posted by RichardSiddaway | with no comments
Filed under:

Advanced Function template

In this post I showed my Advanced Function template  http://msmvps.com/blogs/richardsiddaway/archive/2011/05/13/powershell-module-construction.aspx

I’ve since modified it to add the parameter validation methods. I can never remember them all so decided putting them in the template was the easiest way forward.

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039

function aaaa-yyyyyy{ 
[CmdletBinding(SupportsShouldProcess=$true, 
    ConfirmImpact="Medium Low High None", 
    DefaultParameterSetName="XXXXX")] 
param ( 
[parameter(Position=0,
   Mandatory=$true,
   ParameterSetName="YYYYYYYYYY",
   ValueFromPipeline=$true, 
   ValueFromPipelineByPropertyName=$true,
   ValueFromRemainingArguments=$true,
   HelpMessage="Put your message here",
   Alias("CN", "ComputerName")  )] 
   [AllowNull()]
   [AllowEmptyString()]
   [AllowEmptyCollection()]
   [ValidateCount(1,10)]
   [ValidateLength(1,10)]
   [ValidatePattern("[A-Z]{2,8}[0-9][0-9]")]
   [ValidateRange(0,10)]
   [ValidateScript({$_ -ge (get-date)})]
   [ValidateSet("Low", "Average", "High")]
   [ValidateNotNull()]
   [ValidateNotNullOrEmpty()]
   [string]$computer="." 
) 
BEGIN{}#begin
PROCESS{

if ($psCmdlet.ShouldProcess("## object ##", "## message ##")) {
    ## action goes here
}

}#process
END{}
#end

<#
.SYNOPSIS
.DESCRIPTION
.PARAMETER <Parameter-Name>
.EXAMPLE
.INPUTS
.OUTPUTS
.NOTES
.LINK
#>

}

 

One or two may not be right on a string parameter but they are there as a reminder not code that will run

More Help

I recently mentioned the update to the PowerShell help files that can be downloaded from the Microsoft web site. Tucked away in that file is a page entitled Windows PowerShell Features from Microsoft.

Its worth downloading the help file for this page alone as it contains links to the major PowerShell features including:

  • Core PowerShell cmdlets
  • AD cmdlets
  • BPA cmdlets
  • Exchange Management Shell
  • Clustering cmdlets
  • SC VMM
  • IIS
  • Windows Backup

and many others

Posted by RichardSiddaway | with no comments
Filed under:

Scripting Games Commentary: XIV

One thing that I saw in a script really made me sit up and take notice. The line of code was this

 

write-output $object | format-table

 

I looked at it several times before deciding that while it works it probably doesn’t deliver what the scripter thought it did.

If we look at the definitions of these two cmdlets

write-output

Sends the specified objects to the next command in the pipeline. If the command is the last command in the pipeline, the objects are displayed in the console.

 

format-table

The Format-Table cmdlet formats the output of a command as a table with the selected properties of the object in each column. The object type determines the default layout and properties that are displayed in each column, but you can use the Property parameter to select the properties that you want to see.

 

So we are sending our object ($object) onto the pipeline where format-table will display and output. 

The output is effectively text.

A good rule of thumb is that functions and scripts should output objects

A better solution would be

 

function do-stuff {

 

do lots of stuff

 

write-output $object

}

You can then do

 

do-stuff | format-table

This gives the most flexible combination.

 

Output objects and format as late as possible.

Posted by RichardSiddaway | with no comments
Filed under:

New PAM version

I’ve added the 0.6 release of the PowerShell Admin Modules to codeplex - http://psam.codeplex.com/

This release adds two modules

PAMADSNAPSHOT – functions for work with AD snapshots in Windows 2008 & 2008 R2

PAMLOAD – loads all of the PAM modules in one command

Import-Module PAMLOAD –Force

 

Further releases are planned during the year

Posted by RichardSiddaway | with no comments
Filed under: , ,

Deleting Local User accounts

Do you need to be able to delete local user accounts?  try this

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029

function remove-user {
[CmdletBinding(SupportsShouldProcess=$true)]
param (
 [parameter(ValueFromPipeline=$true,
   ValueFromPipelineByPropertyName=$true)]
 [string]$computer,
 
 [parameter(ValueFromPipeline=$true,
   ValueFromPipelineByPropertyName=$true)]
 [string]$id 
)
BEGIN {Add-Type -AssemblyName System.DirectoryServices.AccountManagement} 
PROCESS {  
 switch ($computer){
  "."    {$computer = $env:computername}
  "localhost" {$computer = $env:computername}
 }
 
 $ctype = [System.DirectoryServices.AccountManagement.ContextType]::Machine
 $context = New-Object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList $ctype, $computer

 $user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($context, $id)

## delete the user
if ($psCmdlet.ShouldProcess("$id", "Will be deleted")) {
    $user.Delete()
  }
} 
}

 

We use the System.DirectoryServices.AccountManagement classes to find the user we want and then call the Delete method.  It works nicely on remote machines assuming you have the required permissions.

Posted by RichardSiddaway | with no comments
Filed under:

Testing network connectivity

One of the standard troubleshooting tasks when investigating a problem is deciding if the machine can communicate on the network. The approach is usually

  • ping the loop back address to check TCP/IP is working
  • ping the machines own address
  • ping the default gateway
  • ping other servers

This means running ipconfig to discover some of the information and  then running pings

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021

function test-networkconnectivity {
[CmdletBinding()]
param()

$nic = Get-WmiObject Win32_NetworkAdapterConfiguration `
-Filter "DHCPEnabled = $true AND IPEnabled = $true"

Write-Verbose "TCP/IP Stack"
Test-Connection -ComputerName 127.0.0.1

Write-Verbose "Local Address"
Test-Connection -ComputerName $nic.IPAddress[0]

Write-Verbose "Default Gateway"
Test-Connection -ComputerName $nic.DefaultIPGateway

Write-Verbose "DNS Server"
foreach ($address in $nic.DNSServerSearchOrder){
Test-Connection -ComputerName $address}

}

 

We can simplify this action.  Use WMI to get the data (I’m assuming we are doing this on a client) from the DHCP enabled NIC. I added the filter for IPEnabled to filter out BlueTooth adapters.

We can then use Test-Connection to perform the pings.  The various results are labelled accordingly if we use the –verbose switch

test-networkconnectivity -Verbose

Posted by RichardSiddaway | with no comments
Filed under:

The simple touch

A utility called touch has been used for many years to modify the creation, access or write time property on a file. The System.Io.FileInfo class enables us to do this. It gets easier because Get-ChildItem returns – guess what - System.Io.FileInfo

The following properties can be modified

CreationTime
LastAccessTime
LastWriteTime

We can create a function to do this

 

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028

function set-filetime{ 
[CmdletBinding()] 
param ( 
[parameter(Mandatory = $true,
 
           ValueFromPipeline
=$true,
 
           ValueFromPipelineByPropertyName
=$true)] 
[string]$path,
[datetime]$date = (Get-Date),
[switch]$creation,
[switch]$access,
[switch]$write
 
) 

PROCESS
{
 
if (Test-Path $path
) {
 
$file = Get-ChildItem -Path $path
  if ($creation){$file.CreationTime = $date
}
 
if ($access){$file.LastAccessTime = $date
}
 
if ($write){$file.LastWriteTime = $date
}
 }
 
else
 {
  
Throw "File $path not found"
 }

}
#process


}
New-Alias -Name touch -Value set-filetime

Setting an alias makes it less typing to use from the command line

Some examples of using it

touch -path d1.txt -creation -access -write                                                                                                   
touch -path d1.txt -write -date (get-date).AddDays(-30)                                                                                       
touch -path d1.txt -access -date (get-date).AddDays(-15)                                                                                      
touch -path d1.txt -creation -date (get-date).AddDays(-50)  

Posted by RichardSiddaway | with no comments

This made me smile

The notice about browser age popped up today.  Look what I’m running.  Do they know something we don’t?

 

browser

Posted by RichardSiddaway | with no comments
Filed under:
More Posts Next page »