Monthly Archive

Categories

Monthly Archives: May 2011

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.

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

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.

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

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.

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

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)  

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