PowerShell for Windows Admins


November 5, 2011  5:56 AM

Working with IPv6

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A comment was left on one of the articles in my recent series on working with the hosts file asking about using IPv6.  I’ve not used it really as I’ve never had any reason but it seems a good idea to dig into it a bit as IPv6 is enabled by default in Windows Vista and above.

Looking at my development machine

PS> Get-WmiObject -Class Win32_NetworkAdapterConfiguration | where {$_.IPAddress} | fl Description, IPAddress, IPSubnet

Description : NVIDIA nForce 10/100/1000 Mbps Networking Controller
IPAddress   : {10.10.54.202, fe80::4547:ee51:7aac:521e}
IPSubnet    : {255.255.255.0, 64}

Description : Atheros AR5007 802.11b/g WiFi Adapter
IPAddress   : {192.168.1.2, fe80::6d95:b824:6a72:a0a9}
IPSubnet    : {255.255.255.0, 64}

 

There are two addresses defined for each adapter. The Ipv4 addresses should be recognisable as two of the private address spaces available in IPv4.

The two IPv6 addresses

fe80::4547:ee51:7aac:521e

fe80::6d95:b824:6a72:a0a9

are known as Link-Local addresses and are equivalent to the APIPA (169.254.x.x) addresses we get when adapters are configured to  get their address from a DHCP server and can’t find it.

IPv6 addresses are 128 bit addresses (IPv4 is 32bit) with each 16bit block converted to hexadecimal and separated by a colon ( : )  which should give us 8 blocks. But there are only 5 blocks in the addresses I found.  That because multiple contiguous blocks of 0000 can be replaced by ::  And just to add to the confusion we can drop leading 0’s on a block.

Time to expand the addresses to see what they really look like

 

function get-IPAddress {            
[CmdletBinding()]            
 param (            
  [string]$computer="$env:COMPUTERNAME"            
 )            
  Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $computer | where {$_.IPAddress} |             
  foreach {            
  $address = $_.IPAddress[1]            
              
  Write-Debug "Test for ::"            
  if ($_.IPAddress[1].Contains("::")){            
   $blocks = $_.IPAddress[1] -split ":"            
   $count = $blocks.Count            
   $replace = 8 - $count + 1            
   for ($i=0; $i -le $count-1; $i++){            
    if ($blocks[$i] -eq ""){            
      $blocks[$i] = ("0000:" * $replace).TrimEnd(":")            
    }            
   }            
   $address = $blocks -join ":"            
  }            
            
  Write-Debug "Check leading 0 in place"            
  $blocks = $address -split ":"            
  for ($i=0; $i -le $blocks.Count-1; $i++){            
    if ($blocks[$i].length -ne 4){            
      $blocks[$i] = $blocks[$i].Padleft(4,"0")            
    }            
   }            
   $address = $blocks -join ":"            
            
   $ipconfig = New-Object -TypeName PSObject -Property @{            
     Description = $($_.Description)            
     IPv4Address = $($_.IPAddress[0])            
     IPv4Subnet = $($_.IPSubnet[0])            
     IPv6Address = $address            
     IPv6Subnet = $($_.IPSubnet[1])            
   } | select Description, IPv4Address, IPv4Subnet, IPv6Address, IPv6Subnet            
   $ipconfig             
  }              
}

Start by using Get-WmiObject on the NetworkAdapterConfiguration class. We only want those adapters where an IP address has been configured.

For each adapter – the first part of the address and subnet is Ipv4 & the second is IPv6

Take the IPv6 address and if it contains “::” we know there are blocks missing.  Split the address on “:” and the “::” generates an empty string which we can replace with the relevant number of “0000” blocks.

Each block in the  address is then tested for length and any leading zeros that might be missing are added back in.

Join the blocks together to create a complete address and display

 

PS> get-IPAddress


Description : NVIDIA nForce 10/100/1000 Mbps Networking Controller

IPv4Address : 10.10.54.202

IPv4Subnet  : 255.255.255.0

IPv6Address : fe80:0000:0000:0000:4547:ee51:7aac:521e

IPv6Subnet  : 64

Description : Atheros AR5007 802.11b/g WiFi Adapter

IPv4Address : 192.168.1.2

IPv4Subnet  : 255.255.255.0

IPv6Address : fe80:0000:0000:0000:6d95:b824:6a72:a0a9

IPv6Subnet  : 64

 

Next time we will look at the IPv6 subnet and what it means

November 2, 2011  2:32 PM

Why use the pipeline?

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

In this post http://msmvps.com/blogs/richardsiddaway/archive/2011/10/31/multiple-value-query-in-wql.aspx I showed how to use Get-WmiObject, WQL and Invoke-WmiMethod to stop multiple services.  The code looks like this

Get-WmiObject -Class Win32_Service -Filter "State=’Running’ AND Name LIKE ‘%BITS%’ OR Name LIKE ‘%WinRM%’" |
Invoke-WmiMethod -Name StopService

 

i was asked why I didn’t use foreach – ie make the PowerShell code look like this

 

Get-WmiObject -Class Win32_Service -Filter "State=’Running’ AND Name LIKE ‘%BITS%’ OR Name LIKE ‘%WinRM%’" |
ForEach-Object { Invoke-WmiMethod -Path $_.__Path –Name StopService }

 

Fine the code works but it is more complicated than we need.

 

lets look at Invoke-WmiMethod

type Invoke-WmiMethod -      then hit the tab key

we get this

Invoke-WmiMethod –InputObject

Now try

Get-Help Invoke-WmiMethod –Full

One of the parametersets looks like this

Invoke-WmiMethod -InputObject [-ArgumentList ] [-Name] [-AsJob] [-ThrottleLim it ] [-Confirm] [-WhatIf] []

Notice that InputObject is the first parameter.

Now try this

PS> Get-Help Invoke-WmiMethod -Parameter InputObject

-InputObject <ManagementObject>
    Specifies a ManagementObject object to use as input. When this parameter is used, all other parameters except the Flag and Argument parameters are ignored.

    Required?                    true
    Position?                    named
    Default value
    Accept pipeline input?       true (ByValue)
    Accept wildcard characters?  false

 

This parameter accepts pipeline input. It is the only parameter for Invoke-WmiMethod that does.

This means that when  Invoke-WMiMethod is on the pipeline and receives an object it will check to see if it is a ManagementObject  object i.e. a WMI type object and if it is it will accept it as input through the InputObject parameter.

Our original pipeline creates some WMI objects using Get-WmiObject.  They are then put on to the pipeline. Invoke-WmiMethod identifies them as WMI objects and tests to see if they have a method called StopService.  They are created from the  Win32_Service so they better have that method.  Invoke-WmiMethod  then invokes the method.

 

The alternative

Get-WmiObject -Class Win32_Service -Filter "State=’Running’ AND Name LIKE ‘%BITS%’ OR Name LIKE ‘%WinRM%’" |
ForEach-Object { Invoke-WmiMethod -Path $_.__Path –Name StopService }

 

effectively does the same BUT involves more work on out part.  The WMI objects are put on the pipeline and then for each one we use the path to the object to identify the object on which we call the StopService method.

In practice the two sets of code do exactly the same – they stop the services. Just using the pipeline requires less typing and therefore less mistakes.

Use the  pipeline – its what it was designed for


October 31, 2011  4:52 PM

Reading the hosts file–revised

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Quick revision to the post on reading the hosts file http://msmvps.com/blogs/richardsiddaway/archive/2011/10/23/reading-the-hosts-file.aspx.

I wanted to be able to display the whole file

function get-hostfilecontent {            
 param ([switch]$all)            
 $file = Join-Path -Path $($env:windir) -ChildPath "system32\drivers\etc\hosts"            
 if (-not (Test-Path -Path $file)){            
   Throw "Hosts file not found"            
 }            
 $cont = Get-Content -Path $file             
 if ($all) {            
   $cont            
 }            
 else {            
   $cont |             
   where {!$_.StartsWith("#")} |            
   foreach {            
     if ($_ -ne ""){            
       $data = $_ -split " ",2            
       New-Object -TypeName PSObject -Property @{            
         Server = $data[1].Trim()            
         IPAddress = $data[0].Trim()            
       }            
     }            
   }            
 }            
}

so added an $all switch.  If this is selected the full contents of the file are displayed otherwise just the IP address entries are displayed as before


October 31, 2011  2:03 PM

Multiple value query in WQL

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A simple query that demonstrates how to query for multiple values. We want to stop the running services that are running where the names a like BITS and WinRm

Get-WmiObject -Class Win32_Service -Filter "State=’Running’ AND Name LIKE ‘%BITS%’ OR Name LIKE ‘%WinRM%’" |
Invoke-WmiMethod -Name StopService

 

Define the service state and use AND to link to the names and then OR to say you want name A or name B.  If it is easier to visualise use the syntax like this

Get-WmiObject -Class Win32_Service -Filter "State=’Running’ AND (Name LIKE ‘%BITS%’ OR Name LIKE ‘%WinRM%’)"

 

It does work!

 

To restart the services

Get-WmiObject -Class Win32_Service -Filter "State=’Stopped’ AND Name LIKE ‘%BITS%’ OR Name LIKE ‘%WinRM%’" |
Invoke-WmiMethod -Name StartService


October 31, 2011  1:46 PM

Logging non-contactable systems

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

In this post – http://msmvps.com/blogs/richardsiddaway/archive/2011/10/23/1760058.aspx – I showed how to get the date of the last update applied to a system.  A comment was posted asking how to log machines that can’t be contacted

".", "rslaptop01", "200.0.0.1" | foreach {              
    if (Test-Path -Path hotfix.log){Remove-Item -Path hotfix.log -Force}            
                
    if(-not(Test-Connection -ComputerName $_ -Count 1 -Quiet)){            
      Add-content -Path hotfix.log -Value "Could not contact $($_) at $(get-date)" -Encoding ASCII            
    }            
    else {            
      Get-HotFix -ComputerName $_  |              
      Where {$_.InstalledOn} |              
      sort InstalledOn -Descending |              
      select CSname, @{Name="Installed";              
      Expression={"{0:dd MMMM yyyy}" -f [datetime]$_.InstalledOn.Tostring()}} -First 1             
    }            
}

Simply add a couple of lines to run Test-Connection and if you don’t get an answer then write out to a log file.


October 30, 2011  2:38 PM

Clearing hosts file

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

We seen how to delete a single entry from the hosts file – this is how we clear all entries

function clear-hostfilecontent {            
 [CmdletBinding()]            
 param ()            
 $file = Join-Path -Path $($env:windir) -ChildPath "system32\drivers\etc\hosts"            
 if (-not (Test-Path -Path $file)){            
   Throw "Hosts file not found"            
 }            
 Write-Verbose "Remove IP Addresses"            
 $data = ((Get-Content -Path $file) -notmatch "^\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")             
             
 $data             
              
 Set-Content -Value $data -Path $file -Force -Encoding ASCII             
}

 

Don’t bother with parameters and change the regex to pick off any lines that don’t start with an IP address (or at least the pattern that represents an IP address).  Write the data back to the file.  I’ve used ASCII encoding on these because the default is Unicode which uses 2 bytes per character and isn’t really usable.


October 26, 2011  2:09 PM

PowerShell, Storage, WMI and Windows Server 8

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

A must read if you are interested in any of these

http://blogs.technet.com/b/server-cloud/archive/2011/10/14/windows-server-8-standards-based-storage-management.aspx

 

WMI is getting everywhere these days – better learn it quick


October 26, 2011  1:49 PM

Remove a host file record

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Next up is removing a record from a hosts file

function remove-hostfilecontent {
 [CmdletBinding()]
 param (
  [parameter(Mandatory=$true)]
  [ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]
  [string]$IPAddress,            

  [parameter(Mandatory=$true)]
  [string]$computer
 )
 $file = Join-Path -Path $($env:windir) -ChildPath "system32\drivers\etc\hosts"
 if (-not (Test-Path -Path $file)){
   Throw "Hosts file not found"
 }
 Write-Verbose "Remove IP Address"
 $data = ((Get-Content -Path $file) -notmatch "$ip\s+$computer")            

 $data             

 Set-Content -Value $data -Path $file -Force -Encoding ASCII
}

Get an IP Address and computer as before. Create the path to the hosts file.

Read the files contents and perform a –notmatch using the IP Address and computername in the regular expression. \s+ means one or more white spaces. This removes the record we don’t want.

I managed to create a regular expression that works Smile

Then write the data back. normally I wouldn’t look at completely re-writing a file like this but the hosts file is small so its probably as quick and it makes the code really simple.


October 25, 2011  3:19 PM

Minor rant

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Why do software suppliers – Adobe with Acrobat Reader & Oracle with Java are the worst culprits – insist on trying to install their browser toolbar & change my default search engine??

What’s worse is that they make the default action to install it & I have to remember each and every time their products update – at least once a week! – to untick the box so it doesn’t install.

 

I really hate this behaviour – please stop.

I know you won’t but I had to ask


October 24, 2011  12:52 PM

Hosts file – add a record

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

We’ve seen how to read the Hosts file – this is how we add a record

function add-hostfilecontent {            
 [CmdletBinding(SupportsShouldProcess=$true)]            
 param (            
  [parameter(Mandatory=$true)]            
  [ValidatePattern("\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b")]            
  [string]$IPAddress,            
              
  [parameter(Mandatory=$true)]            
  [string]$computer            
 )            
 $file = Join-Path -Path $($env:windir) -ChildPath "system32\drivers\etc\hosts"            
 if (-not (Test-Path -Path $file)){            
   Throw "Hosts file not found"            
 }            
 $data = Get-Content -Path $file             
 $data += "$IPAddress  $computer"            
 Set-Content -Value $data -Path $file -Force -Encoding ASCII             
}

 

Take an IP address and computer as parameters.  Test if the hosts file exists

Read the contents, add the new record and write back.

This ensures that the new record is actually on a new line all by itself


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: