PowerShell for Windows Admins


November 15, 2011  2:43 PM

Infrastructure Architecture–from the Middle Ages to Now

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

i often wonder about how we go about performing Infrastructure Architecture in particular and Architecture in general. We spend a lot of time and effort creating frameworks such as Zachman and TOGAF; we create large bodies of data in the form of Enterprise Architecture bodies; we have patterns and reference architecture and the body of white papers and other advertorial content produced by, or on behalf of, vendors.

When it comes down to to actually putting infrastructure on the ground how much of this do we actually use & think about?

Are we like the research scientist looking for  the best answer to solve the problem that we are investigating or are we more like the master mason’s of the Medieval period that built Europe’s great cathedrals, churches and castles?

My feeling is that in many cases we are more like the later.

We have a set of techniques, tricks and tips that we know work because we have used them before. We learn new techniques by working with different people –changing jobs, contractors coming into the organisation  etc.  Often this information ends up being traded. We often serve a long apprenticeship working up through building servers, configuring OS and applications and troubleshooting. When we are deemed worthy – skilled and knowledgeable enough – we are presented with our own projects.

Pretty much parallels the Guild system of the Middle Ages!

Next time you are planning some infrastructure architecture think back on the heritage of how we apply our knowledge. Hopefully one day we will be in the position that it becomes more science than art – when that happens though some of the fun will have gone

November 14, 2011  2:15 PM

Swapping virtual switches

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

Virtualisation is a great technique for creating demo labs. I took my laptop with a bunch of VMs of Windows 8/Server 8 to the PowerShell Deep Dive.  Normally I run my “server” laptop and my development laptop on a switch or connected via a cross over cable. The VM nics are bound to the “server” laptops real ethernet NIC.

This don’t work when you ( a ) forget the cross over cable and ( b ) have the battery collapse on the dev box. I needed to swap the VMs to run off the internal private Hyper-V network.  Swapping the nics between virtual switches is easy but its a pain when you need to do a number of them.

Time for a couple of functions

function set-loopbackswitch {            
            
Get-VM |            
foreach {            
 $nic = Get-VMNIC -VM $_ |             
 where{$_.SwitchName -eq "Local Area Connection - Virtual Network"}             
 Set-VMNICSwitch -NIC $nic -Virtualswitch "LoopBack"            
}            
}            
            
function set-realswitch {            
Get-VM |            
foreach {            
 $nic = Get-VMNIC -VM $_ |             
 where{$_.SwitchName -eq "LoopBack"}             
 Set-VMNICSwitch -NIC $nic -Virtualswitch "Local Area Connection - Virtual Network"            
}            
}

These get the virtual machines and get the nics corresponding to the appropriate switch. It then swaps the the nic to the other switch. Created as 2 functions as its quicker to write.

The functions are based on James O’Neill’s Hyper-V library – if you haven’t got it it is on codeplex


November 13, 2011  1:12 PM

Patch Tuesdays 2012

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

I’ve shown these functions before but as we head rapidly towards 2012 we need to plan next years schedules.  One perennial for Windows administrators is patching – therefore we need to know patch Tuesdays. This is next years offenders

10 January 2012
14 February 2012
13 March 2012
10 April 2012
08 May 2012
12 June 2012
10 July 2012
14 August 2012
11 September 2012
09 October 2012
13 November 2012
11 December 2012

 

How do we arrive at these dates – simple look at a Calendar or better still use these functions

function get-secondTuesday {            
param([datetime]$date)            
    switch ($date.DayOfWeek){            
        "Sunday"    {$patchTuesday = $date.AddDays(9); break}             
        "Monday"    {$patchTuesday = $date.AddDays(8); break}             
        "Tuesday"   {$patchTuesday = $date.AddDays(7); break}             
        "Wednesday" {$patchTuesday = $date.AddDays(13); break}             
        "Thursday"  {$patchTuesday = $date.AddDays(12); break}             
        "Friday"    {$patchTuesday = $date.AddDays(11); break}             
        "Saturday"  {$patchTuesday = $date.AddDays(10); break}             
     }            
     $patchTuesday.ToLongDateString()            
}            
            
function Get-PatchTuesday {            
param (            
    [parameter(ValueFromPipeline=$true)]            
    [int]$year = (Get-Date).Year,            
                
    [switch]$nextmonth            
    )            
                 
    if ($nextmonth){            
        $now = Get-Date            
        $d = Get-Date -Day 1 -Month $($now.Month + 1) -Year $now.Year            
        get-secondTuesday $d            
    }            
    else {            
        1..12 | foreach {            
            $d = [datetime]"$_/1/$year"            
            get-secondTuesday $d            
        }                
    }            
}

We run the function like this

Get-PatchTuesday -year 2012

It takes the year and for each month in the year creates a datetime object for the first of the month.  That date is fed to the get-secondtuesday function which looks at the day of the week and calculates the second Tuesday.

If your patching Window is a specific day in the month then modify the functions to calculate those dates and feed them straight to your change control process


November 11, 2011  2:48 PM

Set registry key owner

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

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

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

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

Richard Siddaway Richard Siddaway Profile: Richard Siddaway

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

Richard Siddaway Richard Siddaway Profile: Richard Siddaway


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

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


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: