Categories

Output from jobs

I tripped over a little problem the other day that’s worth reporting.  I was running something like this:

 

$sb = {
$procs = get-service
$procs | Export-Csv test.csv -NoTypeInformation
}

Start-Job -ScriptBlock $sb -Name test

 

I was collecting some data and outputting a CSV.  My problem was more complex but this stands as a good example

 

I didn’t get the data I wanted

 

Thinking about it I put the full path to where I wanted the CSV

 

$sb = {
$procs = get-service
$procs | Export-Csv C:\MyData\scripts\Test\test.csv -NoTypeInformation
}

Start-Job -ScriptBlock $sb -Name test

 

And it works.

 

So where did my data go in the original version?

 

I ran this

 

$sb = {
Get-Location

$procs = get-service
$procs | Export-Csv test.csv -NoTypeInformation
}

Start-Job -ScriptBlock $sb -Name test

 

And then pulled the data from the job

 

£> Receive-Job -Id 10

Path
----
C:\Users\Richard\Documents

 

Obvious really – a job runs in a new powershell process that doesn’t run your profile so it starts in the default location  - which is your home directory. And sure enough the CSV file is there

 

£> ls C:\Users\Richard\Documents\*.csv


    Directory: C:\Users\Richard\Documents


Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        14/09/2014     11:50      46042 test.csv

 

I can’t remember how many times I’ve told people that PowerShell jobs run in a separate process so I should have realised.  Excellent example of the more you know the more you need to learn

Multiple triggers on a scheduled task

Setting up a scheduled task can be accomplished like this:

 

$taskname = 'Test Job'

Get-ScheduledTask -TaskName $taskname -ErrorAction SilentlyContinue |  Unregister-ScheduledTask -Confirm:$false

 

$trigger = New-ScheduledTaskTrigger -Daily -At 03:00

 

$actionscript = '-NoExit -NonInteractive -NoProfile -WindowStyle Normal -NoLogo -Command "&{get-process}"'

$pstart =  "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

$action = New-ScheduledTaskAction -Execute $pstart -Argument $actionscript
Register-ScheduledTask -TaskName $taskname -Action $action -Trigger $trigger -RunLevel Highest -Description "Test job"

 

Define the task name and then unregister (remove) any instances of that task

 

You then define the trigger and actions and finally register the task with the name you defined earlier.

 

The script above will create a task to run at 3am.

 

You can force the task to run as a test:

Start-ScheduledTask -TaskName 'Test Job'

 

But what about if you want to run the task multiple times during the day. Simple.  You define multiple triggers:

 

$taskname = 'Test Job'

Get-ScheduledTask -TaskName $taskname -ErrorAction SilentlyContinue |  Unregister-ScheduledTask -Confirm:$false

 

$triggers = @()
$triggers += New-ScheduledTaskTrigger -Daily -At 03:00
$triggers += New-ScheduledTaskTrigger -Daily -At 09:00
$triggers += New-ScheduledTaskTrigger -Daily -At 15:00
$triggers += New-ScheduledTaskTrigger -Daily -At 21:00

 

$actionscript = '-NoExit -NonInteractive -NoProfile -WindowStyle Normal -NoLogo -Command "&{get-process}"'

$pstart =  "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

$action = New-ScheduledTaskAction -Execute $pstart -Argument $actionscript
Register-ScheduledTask -TaskName $taskname -Action $action -Trigger $triggers -RunLevel Highest -Description "Test job"

 

 

Define an array and then add the triggers into it. Remember to use the array when you register the task.

 

You can test the triggers on a scheduled task:

Get-ScheduledTask -TaskName 'Test Job' | select -ExpandProperty Triggers

OneNote and XML–finding pages

Pages are towards the bottom of the hierarchy in OneNote – though we still haven’t dived into the content of pages yet.

 

You can find the pages in your notebooks like this:

 

$onenote = New-Object -ComObject OneNote.Application
$scope = [Microsoft.Office.Interop.OneNote.HierarchyScope]::hsPages
[ref]$xml = ''

$onenote.GetHierarchy($null, $scope, $xml)

$schema = @{one="http://schemas.microsoft.com/office/onenote/2013/onenote"}

$xpath = "//one:Notebook/one:Section"
Select-Xml -Xml ([xml]$xml.Value) -Namespace $schema -XPath $xpath |
foreach {
$psitem.Node.Name
}

 

There’s one drawback to this – all you get is the page names:


PowerShell Summit
Comedy
Personal information
Book Series
AD
Applications
CCNA
Exchange
Infrastructure Architecture
Operations
Security
Hyper-V
WMI
PS Deep Dive
Deep Dive US12

 

You need to play around with Xpath a bit more to get a meaningfiul structure – ideally notebook – section – page

OneNote and XML–finding sections

I recently showed how to find the names of your OneNote notebooks.  The next level down is the section.  You can find these sections in a notebook like this:

 

$onenote = New-Object -ComObject OneNote.Application
$scope = [Microsoft.Office.Interop.OneNote.HierarchyScope]::hsPages
[ref]$xml = ''

 

$onenote.GetHierarchy($null, $scope, $xml)

 

$schema = @{one="http://schemas.microsoft.com/office/onenote/2013/onenote"}

 

$xpath = "//one:Notebook/one:Section"
Select-Xml -Xml ([xml]$xml.Value) -Namespace $schema -XPath $xpath |
foreach {
$node = $psitem.Node

$npath = Split-Path -Path $node.Path -Parent
 
$props = [ordered]@{
   Workbook =  Split-Path -Path $npath -Leaf
   Section = $node.Name
}
New-Object -TypeName PSObject -Property $props
}

 

The first part of the script where the application object is created, the scope set and you get the hierarchy is the same as before.  The two scripts diverge when you get to the Xpath you’re going to use.  To find the notebooks you used:

 

$xpath = "//one:Notebook"

 

which means get me any Nodes called one:Notebook

 

To find the section you use:

 

$xpath = "//one:Notebook/one:Section"

 

which means any nodes called one;Section that are children of a one:Notebook node.

 

Remember XML is case sensitive.

 

Once you have the section nodes – which look like this:

 

name             : Quick Notes
ID               : {9EFAE9AC-0388-424A-8211-02E8FFE50666}{1}{B0}
path             : https://d.docs.live.net/43cfa46a74cf3e96/Documents/Personal (Web)/Quick Notes.one
lastModifiedTime : 2014-09-04T17:48:07.000Z
color            : #B7C997
Page             : {OneNote: one place for all of your notes, OneNote Basics}

 

You can extract the data you want. The path property can be used to extract the name of the note book with a little bit of Split-Path magic.

 

Next time you’ll see how to get down to the individual pages

CimInstanceProperties

If you use Get-CimInstance and examine the object returnd – you’ll find some meta-data at the end of the object:

CimClass                                  : root/cimv2:Win32_OperatingSystem
CimInstanceProperties                     : {Caption, Description, InstallDate, Name...}
CimSystemProperties                       : Microsoft.Management.Infrastructure.CimSystemProperties

 

Expand CimInstanceProperties

 

£> Get-CimInstance Win32_OperatingSystem | select -ExpandProperty CimInstanceProperties | fl *

 

And you’ll see an entry like this for each property


Name            : Caption
Value           : Microsoft Windows 8.1 Pro
CimType         : String
Flags           : Property, ReadOnly, NotModified
IsValueModified : False

 

The CimType and Flags attributes are especially useful

 

For instance you can use them to discover which properties can be modified:

 

Get-CimInstance Win32_OperatingSystem |
select -ExpandProperty CimInstanceProperties |
foreach {
$flags =   ($psitem | select -ExpandProperty Flags)  -split ", "  

if ($flags -notcontains 'ReadOnly'){$psitem}
}

 

Remember that other properties may be modifiable via a method on the class.

 

You can find similar information via Get-CimClass

 

$class = Get-CimClass -ClassName Win32_OperatingSystem

$class.CimClassProperties |
foreach {
$flags =   ($psitem | select -ExpandProperty Flags)  -split ", "  

if ($flags -notcontains 'ReadOnly'){$psitem}
}

 

Get-CimClass gives you the qualifiers as well as the flags. Get-CimInstance gives you the value of the property if set.

Two options with slightly different results. All the better to investigate CIM classes

Authorising a DHCP server and creating a scope

I recently re-built my test lab and now need to authorise the DHCP server.

You can test the DHCP server:

£> Get-DhcpServerSetting


IsDomainJoined            : True
IsAuthorized              : False
DynamicBootp              : True
RestoreStatus             : False
ConflictDetectionAttempts : 0
NpsUnreachableAction      : Full
NapEnabled                : False
ActivatePolicies          : True

Authorisation is straightforward

Add-DhcpServerInDC -DnsName server02.manticore.org -IPAddress 10.10.54.201

If you only have a single NIC in your DHCP server you don’t need to worry about the IP address

£> Get-DhcpServerSetting


IsDomainJoined            : True
IsAuthorized              : True
DynamicBootp              : True
RestoreStatus             : False
ConflictDetectionAttempts : 0
NpsUnreachableAction      : Full
NapEnabled                : False
ActivatePolicies          : True

Create a scope and activate it

Add-DhcpServerv4Scope -ComputerName server02 -Name "Manticore Scope"  -StartRange 10.10.54.2 -EndRange 10.10.54.30 -Description "Scope for Manticore domain" -Type DHCP -State Active -SubnetMask 255.255.255.0 -LeaseDuration (New-TimeSpan -Days 2)

ComputerName is the name of the DHCP server otherwise the parameters are self explanatory

To view all scopes

Get-DhcpServerv4Scope -ComputerName server02

To view specific scope

Get-DhcpServerv4Scope -ComputerName server02 -ScopeId 10.10.54.0 | fl

ScopeId          : 10.10.54.0
Name             : Manticore Scope
Description      : Scope for Manticore domain
SuperscopeName   :
SubnetMask       : 255.255.255.0
StartRange       : 10.10.54.2
EndRange         : 10.10.54.30
LeaseDuration    : 2.00:00:00
NapProfile       :
NapEnable        : False
Delay(ms)        : 0
State            : Active
Type             : Dhcp
MaxBootpClients  : 4294967295
ActivatePolicies : True

LAST CALL for European PowerShell Summit 2014

This is the last call for attendee registration for the European PowerShell Summit 2014.


The Summit is in Amsterdam – 29 September to 1 October 2014. Details from the events page http://powershell.org/wp/community-events/summit/.


 


Due to a change in circumstances beyond our control  we have to close public registration on 10 September 2014.


 


If you contact us by 10 September and ask to be able to perform a funds transfer rather than paying on line you have until 15 September 2014 to complete that transaction. No monies or registrations will be accepted after 15 September. We will not accept any new request for paying by money transfer after 10 September.


 


Apologies for the change in dates (the web site states registration is open until 15 September) but our hands have been forced on this.


 


There are still a number of places available so please register quickly if you want to attend.  The more attendees we have the better chance we have of staging a European PowerShell Summit in 2015.



Source: Richard Siddaway

LAST CALL for European PowerShell Summit 2014

This is the last call for attendee registration for the European PowerShell Summit 2014.

The Summit is in Amsterdam - 29 September to 1 October 2014. Details from the events page http://powershell.org/wp/community-events/summit/.

 

Due to a change in circumstances beyond our control  we have to close public registration on 10 September 2014.

 

If you contact us by 10 September and ask to be able to perform a funds transfer rather than paying on line you have until 15 September 2014 to complete that transaction. No monies or registrations will be accepted after 15 September. We will not accept any new request for paying by money transfer after 10 September.

 

Apologies for the change in dates (the web site states registration is open until 15 September) but our hands have been forced on this.

 

There are still a number of places available so please register quickly if you want to attend.  The more attendees we have the better chance we have of staging a European PowerShell Summit in 2015.

Finding a file version

Interesting question on the forum – how to find the file version of IE on remote machines?

 

Get-CimInstance -ClassName CIM_DataFile -Filter "Name = 'C:\\Program Files\\Internet Explorer\\iexplore.exe'"  | select -ExpandProperty Version

 

Use the CIM_dataFile class.  Its one of the few CIM_ classes that doesn’t have a Win32_ equivalent.

 

In this case you know the path to the file – note the \\ as you have to escape a single \ in WMI filters

Finding a file version

Interesting question on the forum – how to find the file version of IE on remote machines?


Get-CimInstance -ClassName CIM_DataFile -Filter “Name = ‘C:\Program Files\Internet Explorer\iexplore.exe'”  | select -ExpandProperty Version


Use the CIM_dataFile class.  Its one of the few CIM_ classes that doesn’t have a Win32_ equivalent.


In this case you know the path to the file – note the \ as you have to escape a single in WMI filters



Source: Richard Siddaway