PowerShell for Windows Admins


November 11, 2011  2:48 PM

Set registry key owner



Posted by: Richard Siddaway
PowerShell v2, Registry

In chapter 7 of PowerShell and WMI I stated that I would post a .NET version of a script to set ownership of a registry key. The WMI method needs Vista or above so we need the .NET version for pre-Vista operating systems.

function set-regkeyowner {            
[CmdletBinding()]            
param (            
 [parameter(Mandatory=$true)]            
 [string]            
 [Validateset(“HKCR”, “HKCU”, “HKLM”, "HKUS", "HKCC")]            
  $hive,            
            
 [parameter(Mandatory=$true)]            
 [string]$key            
)              
PROCESS {             
Write-Verbose "Set Hive"            
switch ($hive){            
 “HKCR” {$reg = [Microsoft.Win32.Registry]::ClassesRoot}            
 “HKCU” {$reg = [Microsoft.Win32.Registry]::CurrentUser}            
 “HKLM” {$reg = [Microsoft.Win32.Registry]::LocalMachine}            
 "HKUS" {$reg = [Microsoft.Win32.Registry]::Users}            
 "HKCC" {$reg = [Microsoft.Win32.Registry]::CurrentConfig}            
}            
            
$permchk = [Microsoft.Win32.RegistryKeyPermissionCheck]::ReadWriteSubTree            
$regrights = [System.Security.AccessControl.RegistryRights]::ChangePermissions            
            
Write-Verbose "Open Key and get access control"            
$regkey = $reg.OpenSubKey($key, $permchk, $regrights)            
$rs = $regkey.GetAccessControl()            
            
Write-Verbose "Create security principal"            
$user = New-Object -TypeName Security.Principal.NTaccount -ArgumentList "Administrators"            
            
$rs.SetGroup($user)             
$rs.SetOwner($user)             
$regkey.SetAccessControl($rs)            
}            
}

Take a hive and key as parameters. Use a switch to set the Registry enumeration and then set the permissions and rights we want. Open the key and get the access controls.

Create a security principal for the Administrators group and set the group and owner in the access control. Use SetAccessControl to change the permissions

November 8, 2011  1:20 PM

Hosts file – add an IPv6 address



Posted by: Richard Siddaway
Network, PowerShell v2

This builds on adding an IPv4 address – http://msmvps.com/blogs/richardsiddaway/archive/2011/10/24/hosts-file-add-a-record.aspx

function add-IPv6hostfilecontent {            
 [CmdletBinding()]            
 param (            
  [parameter(Mandatory=$true)]            
  [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             
}

 

The only difference is that I’ve removed the regex that checks an IPv4 address.  i haven’t been able to figure out a sensible regex for an IPv6 address. if any one wants to post one as a comment I’ll add it to the function with fill credit

The get-hostfilecontent from http://msmvps.com/blogs/richardsiddaway/archive/2011/10/23/reading-the-hosts-file.aspx works with IPv4 and IPv6 addresses

PS> get-hostfilecontent

Server                                       IPAddress                                  
——                                       ———                                  
RSLAPTOP01                                   fe80:0000:0000:0000:4547:ee51:7aac:521e    
RSLAPTOP01                                   10.10.54.202


November 6, 2011  1:50 PM

IPv6 Link local addresses and device identifier



Posted by: Richard Siddaway
Network, PowerShell, Registry, WMI

If I run ipconfig on my system the partial results are this

Wireless LAN adapter Wireless Network Connection:

   Connection-specific DNS Suffix  . : tiscali.co.uk
   Link-local IPv6 Address . . . . . : fe80::6d95:b824:6a72:a0a9%11
   IPv4 Address. . . . . . . . . . . : 192.168.1.2
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.1.1

Ethernet adapter Local Area Connection:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::4547:ee51:7aac:521e%10
   IPv4 Address. . . . . . . . . . . : 10.10.54.202
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . :

 

Notice that the IPv6 addresses have a % sign followed by a number.  This is the device id of the network adapter

If we modify our get-IPAddress function to output the adapter’s deviceid (Index on Win32_NetworkAdapterConfiguration class)

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])            
     DeviceId =  $($_.Index)            
   } | select Description, IPv4Address, IPv4Subnet, IPv6Address, IPv6Subnet, DeviceId            
   $ipconfig             
  }              
}

we get this output

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

DeviceId    : 7

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

DeviceId    : 11

 

These device ids don’t match – whats going on.

If we use the MSNdis_EnumerateAdapter and MSNdis_EnumerateAdapterEX classes as in this function from http://msmvps.com/blogs/richardsiddaway/archive/2011/08/25/network-adapter-details.aspx

We can get the adapter details stored in the registry which uses a different set of indexes!!!

function get-adapter {            
param(            
 [string]$computer="."            
)            
Get-WmiObject -Namespace root\wmi -Class MSNdis_EnumerateAdapter `
-ComputerName $computer |            
foreach {            
 $nic = Get-WmiObject -Namespace root\wmi -Class MSNdis_EnumerateAdapterEx `
-ComputerName $computer -Filter "InstanceName = '$($_.InstanceName)'"            
            
$header = $nic.EnumerateAdapter.Header            
            
 New-Object -TypeName PSobject -Property @{            
  Computer = $_.__SERVER            
  Adapter = $_.InstanceName            
  Device = $_.DeviceName            
  Active = $_.Active            
  Index = $($nic.EnumerateAdapter.IfIndex)            
  NetLuid = $($nic.EnumerateAdapter.NetLuid)            
  Revision = $header.Revision            
  Size = $header.Size            
  Type = $header.Type            
 }            
}            
}

get-adapter | sort Index | Format-Table adapter, Index –AutoSize

produces (truncated output of)

Adapter                                               Index

——-                                               —–

NVIDIA nForce 10/100/1000 Mbps Networking Controller     10

Atheros AR5007 802.11b/g WiFi Adapter                    11

 

which matches the ipconfig results of

Ethernet adapter Local Area Connection:

Link-local IPv6 Address . . . . . : fe80::4547:ee51:7aac:521e%10

Wireless LAN adapter Wireless Network Connection:

   Link-local IPv6 Address . . . . . : fe80::6d95:b824:6a72:a0a9%11

 

We can test the relationship between the registry and WMI device Ids

function resolve-deviceID {            
[CmdletBinding()]            
 param (            
  [string]$computer="$env:COMPUTERNAME"            
 )            
  Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $computer |             
  where {$_.IPAddress} |             
  foreach {            
   $nic = Get-WmiObject -Namespace root\wmi -Class MSNdis_EnumerateAdapterEx `
  -ComputerName $computer -Filter "InstanceName = '$($_.Description)'"            
               
   New-Object -TypeName PSObject -Property @{            
    Name = $($_.Description)            
    WMI_Index = $($_.Index)            
    Reg_Index = $($nic.EnumerateAdapter.IfIndex)            
   }             
 }            
}

Which produces this out put

 

PS> resolve-deviceid | ft -a

Name                                                  Reg_Index WMI_Index

—-                                                  ——— ———

NVIDIA nForce 10/100/1000 Mbps Networking Controller         10         7

Atheros AR5007 802.11b/g WiFi Adapter                        11        11


November 5, 2011  8:04 AM

PowerShell User Group 22 November



Posted by: Richard Siddaway
PowerShell, User Group


When: Tuesday, Nov 22, 2011 9:00 PM (GMT)


Where: Live Meeting

*~*~*~*~*~*~*~*~*~*

PowerShell MVP and PowerCLI Jonathan Medd discusses the new features and PowerCLI 5 and how to get the best from them

Notes


Richard Siddaway has invited you to attend an online meeting using Live Meeting.
Join the meeting.
Audio Information
Computer Audio
To use computer audio, you need speakers and microphone, or a headset.
First Time Users:
To save time before the meeting, check your system to make sure it is ready to use Microsoft Office Live Meeting.
Troubleshooting
Unable to join the meeting? Follow these steps:

  1. Copy this address and paste it into your web browser:
    https://www.livemeeting.com/cc/usergroups/join
  2. Copy and paste the required information:
    Meeting ID: KQ7TKM
    Entry Code: MRW%7bf*P
    Location: https://www.livemeeting.com/cc/usergroups

If you still cannot enter the meeting, contact support

Notice
Microsoft Office Live Meeting can be used to record meetings. By participating in this meeting, you agree that your communications may be monitored or recorded at any time during the meeting.


November 5, 2011  5:56 AM

Working with IPv6



Posted by: Richard Siddaway
Network, PowerShell v2, WMI

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?



Posted by: Richard Siddaway
PowerShell v2, WMI

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



Posted by: Richard Siddaway
Network, PowerShell v2

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



Posted by: Richard Siddaway
PowerShell, WMI

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



Posted by: Richard Siddaway
PowerShell, WMI

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



Posted by: Richard Siddaway
Network, PowerShell v2

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.


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: