UK PowerShell Group November–reminder
UK PowerShell Group meeting – Jonathan Medd on “Whats new in PowerCLI 5”
details from http://msmvps.com/blogs/richardsiddaway/archive/2011/11/05/powershell-user-group-22-november.aspx
Using calculated fields in subsequent processing
A calculated field can be created in Select-Object or Format-Table. When you use Format-Table processing effectively stops, the pipeline terminates and you are dumping the results to screen. The objects produced by Format-Table are not meant for further processing.
If you use Select-Object to create the calculated field the results are an object on the pipeline that can be used for further processing. As a simple example consider
Get-ChildItem -Path c:\windows
This produces the file sizes in bytes. Lets say that we want the sizes in KB
Get-ChildItem -Path c:\windows |
where {!$_.PSIsContainer} |
select Name, @{Name="Size(KB)"; Expression={[math]::round($($_.Length/ 1kb), 2)}}|
Format-Table -AutoSize
OK this is good. I've used the math rounding method as it leaves me with a number rather than a string
No we want to sort of the Size(KB) field so the biggest file is shown first
You might think that
Get-ChildItem -Path c:\windows |
where {!$_.PSIsContainer} |
select Name, @{Name="Size(KB)"; Expression={[math]::round($($_.Length/ 1kb), 2)}}|
sort Size(KB) -Descending |
Format-Table -AutoSize
would work. But we get an error
The term 'KB' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:4 char:13
+ sort Size(KB <<<< ) -Descending |
+ CategoryInfo : ObjectNotFound: (KB:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
When we have a property name that isn't a single simple word we need to put it in quotes like this
Get-ChildItem -Path c:\windows |
where {!$_.PSIsContainer} |
select Name, @{Name="Size(KB)"; Expression={[math]::round($($_.Length/ 1kb), 2)}}|
sort -Property "Size(KB)" -Descending |
Format-Table -AutoSize
To work with properties that we create in calculated fields where their names aren't simple - wrap the name in quotes
Infrastructure Architecture–from the Middle Ages to Now
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
Swapping virtual switches
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
Patch Tuesdays 2012
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
Set registry key owner
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
Hosts file – add an IPv6 address
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
IPv6 Link local addresses and device identifier
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
PowerShell User Group 22 November
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:
- Copy this address and paste it into your web browser:
https://www.livemeeting.com/cc/usergroups/join - 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.
Working with IPv6
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