September 2012 - Posts

Jobs, WMI, CIM and more jobs

Background jobs were one of the most undervalued aspects of PowerShell v2. With the introduction of PowerShell v3 we get ways to schedule those jobs. In this post though I want to look at the new CIM cmdlets and jobs.

Using the WMI cmdlets most PowerShell users have done something like this:

PS> Get-WmiObject -Class Win32_ComputerSystem

Domain              : WORKGROUP
Manufacturer        : Hewlett-Packard
Model               : HP G60 Notebook PC
Name                : RSLAPTOP01
PrimaryOwnerName    : richard_siddaway@hotmail.com
TotalPhysicalMemory : 2951135232

The WMI cmdlets have the ability to be run as a background job by using the –AsJob parameter:

PS> Get-WmiObject -Class Win32_ComputerSystem  -AsJob

Id     Name            PSJobTypeName   State         HasMoreData     Location
--     ----            -------------   -----         -----------     --------
4      Job4            WmiJob          Running       True            localhost

PS> Receive-Job -Id 4 -Keep


Domain              : WORKGROUP
Manufacturer        : Hewlett-Packard
Model               : HP G60 Notebook PC
Name                : RSLAPTOP01
PrimaryOwnerName    : richard_siddaway@hotmail.com
TotalPhysicalMemory : 2951135232


Notice the job type – WMI job

A number of other cmdlets have an –AsJob parameter:

PS> Get-Help * -Parameter *ASJob*

Name
----
Get-NetConnectionProfile
Set-NetConnectionProfile
Invoke-Command
Get-WmiObject
Invoke-WmiMethod
Remove-WmiObject
Restart-Computer
Set-WmiInstance
Stop-Computer
Test-Connection

Notice that the list doesn’t include the CIM cmdlets. If you want to run those as jobs you need to use the standard job cmdlets.

PS> Start-Job -ScriptBlock {Get-CimInstance -ClassName Win32_ComputerSystem}

PS> Get-Job

Id     Name            PSJobTypeName   State
--     ----            -------------   -----
4      Job4            WmiJob          Completed
6      Job6            BackgroundJob   Completed

In this case the job type is Background job not WMI job.

One of the big things with the CIM cmdlets is CIM sessions – these are analogous to remoting sessions and also work over WSMAN

PS> $sw = New-CimSession -ComputerName $env:COMPUTERNAME

PS> Start-Job -ScriptBlock {Get-CimInstance -ClassName Win32_ComputerSystem -CimSession $sw}

PS> Get-Job

Id     Name            PSJobTypeName
--     ----            -------------
4      Job4            WmiJob
6      Job6            BackgroundJob
8      Job8            BackgroundJob

PS> Receive-Job -Id 8 -Keep
Cannot bind argument to parameter 'CimSession' because it is null.
    + CategoryInfo          : InvalidData: (:) [Get-CimInstance], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.Management.Infrastructure.CimCm
   dlets.GetCimInstanceCommand
    + PSComputerName        : localhost

Oops – what happened.

The problem is that back ground jobs run in their own PowerShell process. That’s why you get the prompt back so quick. That process doesn’t contain the CIM session. The way round it is to create the CIM session as part of the job.

PS> $sb ={
>> $sw2 = New-CimSession -ComputerName $env:COMPUTERNAME
>> $sw2
>> Get-CimInstance -ClassName Win32_ComputerSystem -CimSession $sw2
>> }
>>
PS> Start-Job -ScriptBlock $sb

You then get a job that completes and gives you data you can use.

Just remember that if you want to use CIM sessions in a job then create them as part of the job. You should also put in a line to remove them Smile

WMI over WSMAN

Every time I look at PowerShell v3 I seem to find a new way to access WMI!

I’ve covered the –ComputerName and –CimSession parameters before but to recap

We duplicate the way Get-WmiObject works:

$computer = $env:COMPUTERNAME
Get-CimInstance -ClassName Win32_OperatingSystem -ComputerName $computer

 

We can use CIM sessions
$cs = New-CimSession -ComputerName $computer
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $cs

 

One little known and little used capability in PowerShell v2 was the WSMAN cmdlets. These enable us to access WMI directly over WSMAN

$ruri = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_OperatingSystem"
Get-WSManInstance -ResourceURI $ruri -ComputerName $computer

This is fine for a single returned instance but where there are multiples eg disks we need to do a bit more work

$ruri = http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_LogicalDisk

Get-WSManInstance -ResourceURI $ruri -ComputerName $computer -Enumerate |
select DeviceId, Description

It turns out that we can use these ResourceURIs with Get-CimInstance

$ruri = http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_OperatingSystem

Get-CimInstance -ResourceUri $ruri -ComputerName $computer
Get-CimInstance -ResourceUri $ruri -CimSession $cs

Better still if we are going after multiple instances

$ruri = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2/Win32_LogicalDisk"
Get-CimInstance -ResourceUri $ruri -ComputerName $computer |
select DeviceId, Description

Get-CimInstance -ResourceUri $ruri -CimSession $cs |
select DeviceId, Description

You have to use –ComputerName OR –CimSession if you use –ResourceURI.  If you don’t you make a DCOM connection to the local machine and you request will fail. You also won’t be able to use –ResourceURI if your CIM session is over DCOM

PowerShell remoting and upgrading to Windows 2012

I have a number of  virtual machines running Windows 2008 R2 and I upgraded one of them to Windows 2012 – an over the top upgrade rather than a format and clean install

I found that Get-PSSession –Computername didn’t work against that machine.

This parameter now looks at the remote machine endpoint and gets all remoting sessions connected to the machine. I verified that the command worked correctly against a clean install of Windows 2012. This pointed to a WSMAN configuration issue.

I found two issues:

  • PSversion remained on 2.0 after the upgrade
  • SDKversion remained on 1 after the upgrade

You can’t just do a blanket upgrade on SDKversion because some of the endpoints remain on 1 even in a clean Windows 2012 install.

This is what I ended up doing:

Set-Item -Path wsman:\localhost\plugin\microsoft.powershell\InitializationParameters\PSVersion -Value 3.0
Set-Item -Path wsman:\localhost\plugin\microsoft.ServerManager\InitializationParameters\PSVersion -Value 3.0
Set-Item -Path wsman:\localhost\plugin\Microsoft.PowerShell32\InitializationParameters\PSVersion -Value 3.0

Set-Item -Path wsman:\localhost\plugin\Microsoft.PowerShell32\SDKVersion -Value 2
Set-Item -Path wsman:\localhost\plugin\Microsoft.PowerShell\SDKVersion -Value 2
Restart-Service winrm

That didn’t completely fix it so I restarted the remote machine then ran:

Disable-PSRemoting –Force
Enable-PSRemoting –Force

That seemed to fix the problem.

At the moment all seems to be working BUT I’ve more testing to do before I’m 100% happy with this.  This isn’t a procedure I’d recommend unless you exactly what you are doing and why.

Don’t try this at home until you are sure you need to.

Scripting Games 2013

Advanced notice that there will be a Games next year – its changing a bit though.  See http://powershell.org/games/

Posted by RichardSiddaway | with no comments

Using –showwindow

The new –showwindow parameter in Get-Help pops up a little window containing the FULL help file.  Much better than having to rememebr to use –full.  PowerShell immediately returns the prompt so you can continue working – means you can have a number of help files open simultaneously.

Get-Help -Name Get-Process –ShowWindow

This gets to be a bit too much typing – so time to use PowerShell to help and create a function to do the work.

function doc {            
param (            
 [string]$subject            
)            
Get-Help -Name $subject -ShowWindow            
}

pass in the subject and get the help Window.

I’ve added this to my profile for constant access

Posted by RichardSiddaway | with no comments
Filed under:

Change user attribute based on group membership

Want to change an attribute on all members of an Active Directory group.  Then try this

Get-ADGroupMember -Identity Legal |             
where {$_.objectclass -eq "user"} |            
foreach {            
 Set-ADUser -Identity $($_.distinguishedName) -Department "Student"            
}

Get the group members – filter out nested groups by checking the objectclass of the object. For each user set the attribute.

You can test its worked or look at an attribute in a similar way

Get-ADGroupMember -Identity Legal |             
where {$_.objectclass -eq "user"} |            
foreach {            
 Get-ADUser -Identity $($_.distinguishedName) -Property Department |            
 select Name, distinguishedName, Department            
}

You can perform a similar process with the Quest cmdlets

Decoding the mounted device information

In the previous post we looked at how to read the mounted device information. The data is in binary though – if you want it readable and not all of it is readable – try this

$data = @()            
Get-Item -Path HKLM:\SYSTEM\MountedDevices |            
select -ExpandProperty Property |            
where {$_ -like "\Dos*"} |             
foreach {            
 $name = $_            
 $bin = (Get-ItemProperty -Path HKLM:\SYSTEM\MountedDevices -Name $name)."$name"            
             
 $decoded = @()            
 $bin | foreach {            
  $decoded += [char]$_            
 }            
            
            
 $data += New-Object -TypeName psobject -Property @{            
  Device =  $name            
  BinaryValue  = $bin            
  DecodedValue = $($decoded -join "")            
 }            
             
}            
$data | Format-Table  Device, DecodedValue  -AutoSize

Same as last time except for the loop through the binary data using [char] to decode the ASCII values.  use –join to make a string rather than an array. The apparent gaps in the resultant string are because we’re dealing with Unicode

Posted by RichardSiddaway | with no comments
Filed under: ,

Reading mounted device information from the registry

Interesting question about reading the registry.  How do you read HKLM:\SYSTEM\MountedDevices and pull out the name of the device and the associated data.

Get-Item -Path HKLM:\SYSTEM\MountedDevices

returns data of this form

Name                           Property
----                           --------
MountedDevices                 \DosDevices\C: : {218, 187, 32, 142...}
                               \DosDevices\G: : {92, 0, 63, 0...}
                               \DosDevices\E: : {95, 0, 63, 0...}
                               \DosDevices\F: : {92, 0, 63, 0...}
                               \DosDevices\D: : {218, 187, 32, 142...}
                               \DosDevices\I: : {95, 0, 63, 0...}

We need to drill into the property but if we expand the property we will only get the name of the device. So we need to loop through those names

$data = @()            
Get-Item -Path HKLM:\SYSTEM\MountedDevices |            
select -ExpandProperty Property |            
where {$_ -like "\Dos*"} |             
foreach {            
 $name = $_            
$data += New-Object -TypeName psobject -Property @{            
  Device =  $name             
  Value  = (Get-ItemProperty -Path HKLM:\SYSTEM\MountedDevices -Name $name)."$name"            
 }            
            
}            
$data

To simplify the output is limited to Dos devices

Get-itemproperty returns the data with the name of the device as the property name so we need to drill in to get the value

Posted by RichardSiddaway | with no comments
Filed under: ,

Random confusion

There seems to be a bit of cofusion about how Get-Random works.

Try this

1..10 | foreach {Get-Random}

you’ll get  randomly selected 32-bit unsigned integer between 0 (zero) and Int32.MaxValue (0x7FFFFFFF, 2,147,483,647).

The –Minimum parameter sets a minimum value – you will not get any values BELOW this

try this

1..10 | foreach {Get-Random -Minimum 1000}

The –Maximum parameter sets the ceiling on returned values – you will not get any values AT OR ABOVE this value

try this

1..10 | foreach {Get-Random -Maximum 1000}

used in conjunction they define a range of values from which a random value is chosen

try this

1..10 | foreach {Get-Random -Minimum 500 -Maximum 1000}

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell Summit 2013 dates

The dates for the summit have been announced – 22-24 April 2013 @ Microsoft campus Redmond

You can register at http://powershell.org/summit

1/7 th of the tickets have already gone – don’t miss out.

Posted by RichardSiddaway | with no comments
Filed under: ,

Finding the drive letter of a mounted VHD

In Windows 8/2012 you can mount a VHD into the file system. Is there a way to discover the drive letter of the mounted VHD

function get-mountedvhdDrive {            
$disks = Get-CimInstance -ClassName Win32_DiskDrive | where Caption -eq "Microsoft Virtual Disk"            
foreach ($disk in $disks){            
 $vols = Get-CimAssociatedInstance -CimInstance $disk -ResultClassName Win32_DiskPartition             
 foreach ($vol in $vols){            
   Get-CimAssociatedInstance -CimInstance $vol -ResultClassName Win32_LogicalDisk |            
   where VolumeName -ne 'System Reserved'            
 }            
}            
}

Use Get-CimInstance to get the Win32_DiskDrive class. Filter on caption equalling "Microsoft Virtual Disk"

for each “physical” disk returned get the associated Win32_Volume and use that to get the associated Win32_Logical disk where you will find the drive letter.

Nice example of using associations in the CIM cmdlets

PowerShell Summit Open for registration

The PowerShell summit to be held April 2013 at Microsoft’s Redmond campus is now open for early registration.  There are very good deals available for early registration.

Details and registration link here - http://powershell.org/summit/registration.php

Posted by RichardSiddaway | with no comments
Filed under: ,

Office 2013, Windows and SkyDrive app revisited

This is not a combination I would recommend based on the current preview.  If the experience doesn’t get better before Office 2013 RTMs I won’t be upgrading.  Every time I open a document I get a message that there is a problem with the document. Is there a problem? In reality no.

Trying to open a second Word document and your machine freezes for a while.

I have lost work because of the interaction between Word’s attempts to communicate with SkyDrive and the Skydrive app.

I’ll persevere a bit more but as I don’t have anything like the problems with Office 2010 I may just revert

Posted by RichardSiddaway | with no comments
Filed under:

PowerShell Summit April 2013

A PowerShell summit is being proposed for April 2013.  It will be held on the Microsoft campus in Redmond.

Preliminary information can be found here

http://powershell.org/summit/

We’d like your help with this   by answering a s very short survey

http://674004.polldaddy.com/s/powershell-summit-na-2013

Your feed back will help us get it right

Thanks

Posted by RichardSiddaway | with no comments
Filed under: ,

PowerShell v3 download

The download for PowerShell v3 – now know as Windows Management Framework as you get PowerShell, WinRM and new WMI API is now available from

http://www.microsoft.com/en-us/download/details.aspx?id=34595

Versions are available for:

  • Windows 7 SP1
  • Windows 2008 R2 SP1
  • Windows 2008 SP2

No support for Windows 2003, Vista or XP

Posted by RichardSiddaway | with no comments
Filed under:

Finding if the user associated with a profile is logged on

A forum question asked how to find if the user to whom a profile belonged was logged on. There isn’t an easy way as there isn’t an association between the profile and the log on session.

There is a quick and dirty way  though

Get-WmiObject -Class Win32_UserProfile |            
foreach {            
            
$filt =  Split-Path -Path $($_.LocalPath) -Leaf            
$loggedon = $null            
$loggedon = Get-WmiObject Win32_loggedonuser |             
 where {$_.Antecedent -like "*Name=*$filt*"} |             
 select -First 1             
            
$log = $false            
if ($loggedon){$log = $true}            
            
New-Object -TypeName PSObject -Property @{            
 Name = $filt            
 Logged = $log            
}            
}

get the profiles and for each split the Localpath (path to profile) – the leaf holds the user name

Test if you can find an instance of Win32_LoggedOnUser where the Antecedent contains the name

Display data.  Results look like this

Name           Logged
----           ------
DefaultAppPool  False
Richard          True
NetworkService  False
LocalService    False
systemprofile   False

need to filter the system accounts out but I’ll leave that to you

Posted by RichardSiddaway | with no comments
Filed under:

New PowerShell site

If you haven’t found it already I would recommend you visit powershell.org at http://powershell.org/wp/home/.

Its a new location for getting answers to your PowerShell questions.

You will probably recognise some of the people moderating the forums there – Don Jones, Jeff Hicks, Kirk Munro & me among others.

Highly recommended as a PowerShell forum

Posted by RichardSiddaway | with no comments
Filed under:

Windows 8 RTM startup

I was looking at Win32_OperatingSystem today and noticed that the last boot up time wasn’t right

PS> Get-CimInstance -ClassName Win32_OperatingSystem | select LastBootUpTime

LastBootUpTime
--------------
17/08/2012 10:02:11

17 August isn’t right as I know I did a cold start this morning – or at least I think I did!

Windows 8 starts up much faster than Windows 7

Looking at the System event log the last but one entry last night was event id 64 – “The system is entering sleep.”

Looks like using the Shutdown option from Settings – Power only puts the machine to sleep.

I tried using the PowerShell stop-computer cmdlet.

Shutdown took longer than usual. 

I found an entry in the system log stating

“The kernel power manager has initiated a shutdown transition.”

Startup then took much longer than usual

PS> Get-CimInstance -ClassName Win32_OperatingSystem | select LastBootUpTime

LastBootUpTime
--------------
02/09/2012 19:27:41

So looks like use the Settings – Power – Shutdown option to put the machine into a deep sleep and get very quick start up.  Use stop-computer and completely shutdown but have longer startup times.

Question now is does the deep sleep put any drain on the battery for laptops? I’ll try and find out.

Posted by RichardSiddaway | with no comments
Filed under:

Office 2013 and SkyDrive

Office 2013 has the assumption that your SkyDrive is your primary storage area.

Live Essentials supplies an app that allows you to synchronise your PCs with your SkyDrive – it replaces Live Mesh. 

I liked Live Mesh as I could perform peer to peer synchronisation at the folder level.

The new SkyDrive synchronisation only allows a single top level folder and you have to sync through SkyDrive

The interaction between Office 2013 and SkyDrive is not perfect. Office 2013 appears not to understand the SkyDrive and attempts its own synchronisation which doesn’t always work. You are then left with the option to discard your changes or save to another location.

The misunderstandings between SkyDrive and Office 2013 can lead to lost work  - save often!

I haven’t seen as many issues between SkyDrive and Office 2013

This needs to be ironed out before Office 2013 RTMs ideally with a simple option to switch off Office 2013s direct interaction with SkyDrive

Posted by RichardSiddaway | with no comments
Filed under: