Hey, Scripting Guy! Quick-Hits Friday: The Scripting Guys Respond to a Bunch of Questions (6/18/10)

ScriptingGuy1

Bookmark and Share

In this post:

 

In Windows PowerShell, How Can I Pipe the Contents of a Property to a Command?

Hey, Scripting Guy! Question

 

Hey, Scripting Guy! I’m working with some Windows PowerShell where the first command results in a collection of objects that have a property Name. I want to take the contents of the Name property and pipe it to a second command. If I do this, I get the desired result, but without using the pipeline:

$aggr1_volumes=get-navol -Controller $SIM01 | ? {$_.ContainingAggregate -eq "aggr1"}

get-nasnapmirror $aggr1_volumes.Name -Controller $SIM02 |

How would I, instead of using the variable and running the second command, just pipe the Name property into it? Something like the following code:

Get-navol –Controller $SIM01 | ? {$-.ContainingAggregate –eq “aggr1”} | get-nasnapmirror <what goes here to get the .name property of each object in the output from the previous command?> -Controller $SIM02


— JF 
 

Hey, Scripting Guy! AnswerHello JF,

 

To explain this, let us first start a couple of processes:

PS C:\> notepad

PS C:\> notepad


If we attempt to pipe the results from the Where-Object cmdlet directly into the Stop-Process cmdlet, an error is generated. This is shown here:

PS C:\> Get-Process | ? { $_.name -eq "notepad" } | Stop-Process $_

Stop-Process : Cannot bind argument to parameter ‘Id’ because it is null.

At line:1 char:57

+ Get-Process | ? { $_.name -eq "notepad" } | Stop-Process <<<<  $_

    + CategoryInfo          : InvalidData: (:) [Stop-Process], ParameterBindingValidationException

    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.

   Commands.StopProcessCommand


If we attempt to use the ID property from the $_ current object on the pipeline, an error is also generated. Interestingly, this is the same error that was previously generated. This is shown here:

PS C:\> Get-Process | ? { $_.name -eq "notepad" } | Stop-Process $_.id

Stop-Process : Cannot bind argument to parameter ‘Id’ because it is null.

At line:1 char:57

+ Get-Process | ? { $_.name -eq "notepad" } | Stop-Process <<<<  $_.id

    + CategoryInfo          : InvalidData: (:) [Stop-Process], ParameterBindingValidationException

    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.

   Commands.StopProcessCommand


If we look at the members of the object that is coming from the Where-Object cmdlet, we can see that it is an instance of the System.Diagnostics.Process .NET Framework class. This is shown here:

PS C:\> Get-Process | ? { $_.name -eq "notepad" } | GM

   TypeName: System.Diagnostics.Process 

Name                       MemberType     Definition

—-                       ———-     ———-

Handles                    AliasProperty  Handles = Handlecount

Name                       AliasProperty  Name = ProcessName

NPM                        AliasProperty  NPM = NonpagedSystemMemorySize

PM                         AliasProperty  PM = PagedMemorySize

VM                         AliasProperty  VM = VirtualMemorySize

WS                         AliasProperty  WS = WorkingSet

Disposed                   Event          System.EventHandler Disposed(System.Object, System.Eve…

ErrorDataReceived          Event          System.Diagnostics.DataReceivedEventHandler ErrorDataR…

Exited                     Event          System.EventHandler Exited(System.Object, System.Event…

OutputDataReceived         Event          System.Diagnostics.DataReceivedEventHandler OutputData…

BeginErrorReadLine         Method         System.Void BeginErrorReadLine()

BeginOutputReadLine        Method         System.Void BeginOutputReadLine()

CancelErrorRead            Method         System.Void CancelErrorRead()

CancelOutputRead           Method         System.Void CancelOutputRead()

Close                      Method         System.Void Close()

CloseMainWindow            Method         bool CloseMainWindow()

CreateObjRef               Method         System.Runtime.Remoting.ObjRef CreateObjRef(type reque…

Dispose                    Method         System.Void Dispose()

Equals                     Method         bool Equals(System.Object obj)

GetHashCode                Method         int GetHashCode()

GetLifetimeService         Method         System.Object GetLifetimeService()

GetType                    Method         type GetType()

InitializeLifetimeService  Method         System.Object InitializeLifetimeService()

Kill                       Method         System.Void Kill()

Refresh                    Method         System.Void Refresh()

Start                      Method         bool Start()

ToString                   Method         string ToString()

WaitForExit                Method         bool WaitForExit(int milliseconds), System.Void WaitFo…

WaitForInputIdle           Method         bool WaitForInputIdle(int milliseconds), bool WaitForI…

__NounName                 NoteProperty   System.String __NounName=Process

BasePriority               Property       System.Int32 BasePriority {get;}

Container                  Property       System.ComponentModel.IContainer Container {get;}

EnableRaisingEvents        Property       System.Boolean EnableRaisingEvents {get;set;}

ExitCode                   Property       System.Int32 ExitCode {get;}

ExitTime                   Property       System.DateTime ExitTime {get;}

Handle                     Property       System.IntPtr Handle {get;}

HandleCount                Property       System.Int32 HandleCount {get;}

HasExited                  Property       System.Boolean HasExited {get;}

Id                         Property       System.Int32 Id {get;}

MachineName                Property       System.String MachineName {get;}

MainModule                 Property       System.Diagnostics.ProcessModule MainModule {get;}

MainWindowHandle           Property       System.IntPtr MainWindowHandle {get;}

MainWindowTitle            Property       System.String MainWindowTitle {get;}

MaxWorkingSet              Property       System.IntPtr MaxWorkingSet {get;set;}

MinWorkingSet              Property       System.IntPtr MinWorkingSet {get;set;}

Modules                    Property       System.Diagnostics.ProcessModuleCollection Modules {get;}

NonpagedSystemMemorySize   Property       System.Int32 NonpagedSystemMemorySize {get;}

NonpagedSystemMemorySize64 Property       System.Int64 NonpagedSystemMemorySize64 {get;}

PagedMemorySize            Property       System.Int32 PagedMemorySize {get;}

PagedMemorySize64          Property       System.Int64 PagedMemorySize64 {get;}

PagedSystemMemorySize      Property       System.Int32 PagedSystemMemorySize {get;}

PagedSystemMemorySize64    Property       System.Int64 PagedSystemMemorySize64 {get;}

PeakPagedMemorySize        Property       System.Int32 PeakPagedMemorySize {get;}

PeakPagedMemorySize64      Property       System.Int64 PeakPagedMemorySize64 {get;}

PeakVirtualMemorySize      Property       System.Int32 PeakVirtualMemorySize {get;}

PeakVirtualMemorySize64    Property       System.Int64 PeakVirtualMemorySize64 {get;}

PeakWorkingSet             Property       System.Int32 PeakWorkingSet {get;}

PeakWorkingSet64           Property       System.Int64 PeakWorkingSet64 {get;}

PriorityBoostEnabled       Property       System.Boolean PriorityBoostEnabled {get;set;}

PriorityClass              Property       System.Diagnostics.ProcessPriorityClass PriorityClass …

PrivateMemorySize          Property       System.Int32 PrivateMemorySize {get;}

PrivateMemorySize64        Property       System.Int64 PrivateMemorySize64 {get;}

PrivilegedProcessorTime    Property       System.TimeSpan PrivilegedProcessorTime {get;}

ProcessName                Property       System.String ProcessName {get;}

ProcessorAffinity          Property       System.IntPtr ProcessorAffinity {get;set;}

Responding                 Property       System.Boolean Responding {get;}

SessionId                  Property       System.Int32 SessionId {get;}

Site                       Property       System.ComponentModel.ISite Site {get;set;}

StandardError              Property       System.IO.StreamReader StandardError {get;}

StandardInput              Property       System.IO.StreamWriter StandardInput {get;}

StandardOutput             Property       System.IO.StreamReader StandardOutput {get;}

StartInfo                  Property       System.Diagnostics.ProcessStartInfo StartInfo {get;set;}

StartTime                  Property       System.DateTime StartTime {get;}

SynchronizingObject        Property       System.ComponentModel.ISynchronizeInvoke Synchronizing…

Threads                    Property       System.Diagnostics.ProcessThreadCollection Threads {get;}

TotalProcessorTime         Property       System.TimeSpan TotalProcessorTime {get;}

UserProcessorTime          Property       System.TimeSpan UserProcessorTime {get;}

VirtualMemorySize          Property       System.Int32 VirtualMemorySize {get;}

VirtualMemorySize64        Property       System.Int64 VirtualMemorySize64 {get;}

WorkingSet                 Property       System.Int32 WorkingSet {get;}

WorkingSet64               Property       System.Int64 WorkingSet64 {get;}

PSConfiguration            PropertySet    PSConfiguration {Name, Id, PriorityClass, FileVersion}

PSResources                PropertySet    PSResources {Name, Id, Handlecount, WorkingSet, NonPag…

Company                    ScriptProperty System.Object Company {get=$this.Mainmodule.FileVersio…

CPU      &#160;                 ScriptProperty System.Object CPU {get=$this.TotalProcessorTime.TotalS…

Description                ScriptProperty System.Object Description {get=$this.Mainmodule.FileVe…

FileVersion                ScriptProperty System.Object FileVersion {get=$this.Mainmodule.FileVe…

Path                       ScriptProperty System.Object Path {get=$this.Mainmodule.FileName;}

Product                    ScriptProperty System.Object Product {get=$this.Mainmodule.FileVersio…

ProductVersion             ScriptProperty System.Object ProductVersion {get=$this.Mainmodule.Fil…

 

But we already know that just attempting to access the members directly fails. This is because of the way the Where-Object cmdlet interrupts the pipeline. To work with a property directly on the other side of the pipeline from the Where-Object, it is necessary to use the Foreach-Object cmdlet. This is shown here:

PS C:\> Get-Process | ? { $_.name -eq "notepad" } | % { Stop-Process $_.id }

PS C:\>

 

 

Using WMI for Security Event Log Tracing

Hey, Scripting Guy! Question

Hey, Scripting Guy! I am working on a script to do security event log tracing with tight date/time range queries for good performance. I have used WMI for this, but the problem is that I wrote this script for a team that does not have Domain Admins rights, but needs to use WMI to read domain controller logs. I know I can adjust WMI security settings to fix this, but is there an easy way to do this for a few hundred domain controllers by scripting it? I would hate to do this one at a time, and other methods of reading the logs are proving far too slow. The code below is what I am talking about:

(get-eventlog and system.diagnostics.eventlog)

— NL

 

Hey, Scripting Guy! AnswerHello NL,

You know that to grant people access to remote WMI information, they must be a member of the local administrators group by default. This article on MSDN talks about remote WMI security. If you do not wish to do this, you will need to add them to a group and explicitly grant them access to a WMI namespace. On Windows Vista and later, this can be done by using the SetSecurityDescriptor method from the __SystemSecurity class. An instance of this class exists for each namespace in WMI. Namespace security is shown in the following image.

Image of namespace security

You cannot, however, grant them permission to only the Eventlog classes. They will have access to all the WMI classes in the namespace. You could create your own WMI namespace, but that is a different story.

The __SystemSecurity class is documented on MSDN. You can find information about the SetSecurityDescriptor method there as well. The SetSecurityDescriptor method requires an instance of a Win32_SecurityDescriptor class for its argument. This class is in turn composed of two other classes: Win32_ACE and Win32_Trustee. You would have to use Win32_ACE to provide the DACLS and the SACLS you wish to grant to your group. Use Win32_Trustee to specify the group name and the owner of the group. After you have created the Win32_SecurityDescriptor, it is a pretty simple matter to call the SetSecurityDescriptor method.

Before the SetSecurityDescriptor method became available in Windows Vista, one trick I used to use was to use the SetSD method. This method has been available since Windows 2000, and is described in my Microsoft Press book, Windows Scripting with WMI: Self-Paced Learning Guide. What you do is configure the WMI namespace permissions you wish to create by using the WMI Control Tool on a test machine. You then use the GetSD method from the __SystemSecurity class to copy the byte array that represents the security for the namespace. Save this value, and then use the SetSD method to apply the security to the other machines.

As a matter of fact, because of the difficulty inherit in properly managing the three different WMI classes required to manually create an instance of the Win32_SecurityDescriptor WMI class, you may wish to use a similar approach to creating your Security Descriptor. Manually set the namespace permissions on a test machine, use the GetSecurityDescriptor WMI method from the __SystemSecurity class, and then set it using the SetSecurityDescriptor method.

All of this is only half of the solution. The other task is to set the DCOM permissions to allow the group remote access to WMI. In Windows Vista and later, there is the Win32_DCOMApplicationSetting WMI class that has several methods that will allow you to remotely configure the DCOM security settings. The methods you would be most interested in are SetLaunchSecurityDescriptor and SetAccessSecurityDescriptor.

Keep in mind, that when you write your script, you will need to specify the SeSecurityPrivilege and the SeRestorePrivilege in VBScript. In Windows PowerShell 2.0, you simply add the -EnableAllPrivileges switch.

On a Windows Server 2003 or earlier operating system, the Win32_DCOMApplicationSetting WMI class does not exist. There is sample code in the Windows Server 2008 Software Development Kit (SDK) called DCOMPERM that you could compile into an executable and use to make the DCOM settings.

 

Well, this concludes another edition of Quick-Hits Friday. Join us tomorrow for the Weekend Scripter as we delve into the mysteries of…well, we will let that remain a mystery for now.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

 

Ed Wilson and Craig Liebendorfer, Scripting Guys