Latest Updates to PoshRSJob v1.5.7.1

The past few weeks have been about updating some of my modules such as PoshWSUS and now PoshRSJob. With PoshRSJob, I spent a lot of the time focused on improving the PowerShell V2 support by fixing some bugs that had been reported but I also took on a request to provide support for the module to run on Nano server.

Originally, I used Add-Type to compile and load some C# code to provide the classes that the module will use. Unfortunately, Nano server doesn’t have all of the cmdlets available that we know and love and includes Add-Type. I took one approach by using reflection to build out the classes and while this worked outside of Nano, it ultimately didn’t really…yet. I didn’t want to spend a lot of time on that just yet, so I decided to build a dll that would get loaded with the module to create the classes. After fixing that, I had to fix a spot where the runspace.ApartmentState was set to STA to support UIs as Nano doesn’t appear to have an ApartmentState property available to set.

NanoPoshRSJob

The release notes is available below along with the link to the GitHub page where you can download the module or help contribute if you want!

https://github.com/proxb/PoshRSJob

Release Notes

  • Fixed Issue #64 (HadErrors in PoshRS.PowerShell.RSJob throws errors in PowerShell V2)
  • Fixed Issue #67 (Converted Add-Type code for C# classes to be created via Reflection for Nano server support) <- Created custom dll
  • Fixed Issue #61 (Receive-RSJob not allowing -Job parameter input)
  • Fixed Issue #63 (Replaced Global variables with Script scope)
  • Fixed Issue #66 (Parameters don’t work with PowerShell V2)
  • Fixed Issue #65 (Bug with v2 variable substitution – single-quoted strings get $var references replaced)
  • Fixed Issue #68 (ApartmentState Does Not Exist in Nano)
Posted in powershell | Tagged , , | 7 Comments

PoshWSUS 2.3.1.0 Released

I just pushed out a new update to my WSUS module called PoshWSUS. This was mostly a bug fix push as I had found several bugs both on my own and those reported by the community. I also removed a couple of functions that I felt were either being duplicated elsewhere or just not needed. Check the release notes below for more information and of course, if you happen to find any bugs or would like to see something added (or want to add stuff yourself), head over to GitHub and submit an issue or fork it to your own Repo and make a Pull Request!

Links

PoshWSUS Release Page

PoshWSUS Front Page

PoshWSUS Issues

Release Notes

————-
|MISC Updates|
————-
Updated Type and Format files
Changed Global scope WSUS variable to Script
Added Script scope _wsusconfig variable

——————-
|Updated Functions|
——————-
Get-PSWSUSUpdateApproval
Disconnect-PSWSUSServer
Connect-PSWSUSServer
Disconnect-PSWSUSServer
Get-PSWSUSConfigEnabledUpdateLanguages
Get-PSWSUSConfigSupportedUpdateLanguages
Get-PSWSUSConfigUpdateFiles
Get-PSWSUSConfigUpdateSource
Set-PSWSUSConfigEnabledUpdateLanguages
Set-PSWSUSConfigProxyServer
Set-PSWSUSConfigTargetingMode
Set-PSWSUSConfigUpdateFiles
Set-PSWSUSConfigUpdateSource
Get-PSWSUSClientGroupMembership
Get-PSWSUSCommand
Get-PSWSUSEvent
Get-PSWSUSSyncHistory
Resume-PSWSUSUpdateDownload
Remove-PSWSUSClientFromGroup
Remove-PSWSUSClient
Add-PSWSUSClientToGroup
Get-PSWSUSClientPerUpdate

——————-
|Removed Functions|
——————-
Get-PSWSUSSyncEvent
Resume-PSWSUSDownload

Posted in powershell | Tagged , , | 5 Comments

Creating a Robust Services Function

I had a time when I needed to know whether or not a service had triggers, which would mean that the StartMode could be Auto (Triggered Start) so that the service might just stop for its own reason and if I happened to have a script that looked for non-running services set to Automatic, then it would be flagged in a report. After some research, I found that I could hop into the registry and look at for the TriggerInfo folder to determine if the service actually had any triggers. If the folder exists, then it has triggers and if not…well you get the idea! Smile

SNAGHTML3d8704b

If you work in PowerShell, you know that this is a simple query either through the registry provider or using some .Net types to make the remote registry connection and see if the folder exists.

But that’s not where this ends for me. While searching around, I realized that I could query this information using sc.exe as shown below.

image

Where is all of that info at? It sure isn’t in the registry or in WMI that I could find! Somewhere in the operating system, this information is available for us to read. The problem is how do I go about finding it? Do I use sc.exe and go crazy with string parsing in hopes that my code isn’t fragile and works each time regardless of the data returned or do I dig deeper into the windows API and see what kind of wonders are available to me?

The first place to start looking at in regards to windows APIs is pinvoke.net. This site allows you to search for various functions that are available as a C# signature to copy and paste into PowerShell. I’m not using C# and Add-Type in my code and will be dynamically building each method, enum and structure…which will make my line count in this function grow to crazy levels…such as ~1600 lines of code.

In an effort to avoid crushing this article with a lot of code, I am going to pick out a couple of pieces that stood out to me as either a lessons learned experience or something neat to point out. With that, I am going to start off by showing you the methods required to bring all of this together.

image

Querying service information doesn’t just rely on a single command or method, but in fact requires a team of methods to handle specific items that we can then bring together in the end. Speaking of a team of methods, we also have a team of Structs which are needed by the methods to properly pull back the service information.

image

Each Struct is used in the final output of our service query. Some present very straight forward data while others required a little more coaxing in order to properly provide the data needed.

One thing that I ran into with building enums was figuring out how to build a bit flag enum. Fortunately I figured out this which spawned this article talking about the subject.

Backing up a second, the Param block for this function looks like this;

Param (
    [parameter(ValueFromPipelineByPropertyName=$True)]
    [string[]]$Name,
    [parameter(ValueFromPipelineByPropertyName=$True)]
    [Alias('CN','__Server','PSComputername')]
    [string[]]$Computername = $env:COMPUTERNAME,
    [parameter()]
    [ValidateSet('Win32','Win32OwnProcess','Win32ShareProcess','FileSystemDriver',
    'KernelDriver','All','Interactive')]
    [string]$ServiceType = 'Win32'
)

I am trying to keep some things the same as what you have with Get-Service but still wanted to bring in something new in the ServiceType. The ServiceType parameter accounts for the various types of services that you will encounter that you normally do not see when using existing cmdlets. Win32 is the default value as that matches what you see using Get-Service and working with the Win32_Service class.

Depending on if I specify a filter via Name or not will determine whether specific services or all services are displayed to you in the console.

The big first step in all of this is connecting to the Service Manager so that I can then use the handle that is returned to query more things about the services. I found after some trial and error that I need some specific rights which are SC_MANAGER_CONNECT, SC_MANAGER_ENUMERATE_SERVICE and SC_MANAGER_QUERY_LOCK_STATUS. More about these rights can be found here.

#region Open SCManager
Write-Verbose 'Opening Service Manager'
$SCMHandle = [Service.Trigger]::OpenSCManager(
    $Computer, 
    [NullString]::Value, 
    ([SCM_ACCESS]::SC_MANAGER_CONNECT -BOR [SCM_ACCESS]::SC_MANAGER_ENUMERATE_SERVICE -BOR [SCM_ACCESS]::SC_MANAGER_QUERY_LOCK_STATUS)
)
$LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
If ($SCMHandle -eq [intptr]::Zero) {
    Write-Warning ("{0} ({1})" -f $LastError.Message,$LastError.NativeErrorCode)
    Break    
}
Write-Debug "SCManager Handle: $SCMHandle"
#endregion Open SCManager

After getting the handle I come to a point to whether I specified a particular name or will just go forward and bring back all of the services. Let’s just assume that I am looking to return all of the services.

I am going to call EnumServicesStatusEx to get a list of all of the services. This is actually an interesting method because I have to call it twice. The first time to get the required amount of bytes that will be used in a buffer and again using that byte buffer to get all of the services.

If (-NOT $PSBoundParameters.ContainsKey('Name')) {
    #region Enum Services
    Write-Verbose 'Get all services'
    $BytesNeeded = 0
    $ServicesReturned = 0
    $ResumeHandle = 0
    $Return = [Service.Trigger]::EnumServicesStatusEx(
        $SCMHandle,
        [SC_ENUM_TYPE]::SC_ENUM_PROCESS_INFO,
        [SERVICE_TYPE]$ServiceType,
        [SERVICE_STATES]::SERVICE_ALL,
        [IntPtr]::Zero,
        0, # Current Buffer
        [ref]$BytesNeeded,
        [ref]$ServicesReturned,
        [ref]$ResumeHandle,
        [NullString]::Value
    )
    $LastError = [ComponentModel.Win32Exception][Runtime.InteropServices.Marshal]::GetLastWin32Error()
    Write-Debug "BytesNeeded: $BytesNeeded"
    If ($LastError.NativeErrorCode -eq 234) { #More data is available - Expected result
        $Buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($BytesNeeded)
        $Return = [Service.Trigger]::EnumServicesStatusEx(
            $SCMHandle,
            [SC_ENUM_TYPE]::SC_ENUM_PROCESS_INFO,
            [SERVICE_TYPE]$ServiceType,
            [SERVICE_STATES]::SERVICE_ALL,
            $Buffer,
            $BytesNeeded, # Current Buffer
            [ref]$BytesNeeded,
            [ref]$ServicesReturned,
            [ref]$ResumeHandle,
            [NullString]::Value
        )

As long as I get the expected error back that more data is needed, I can call the method again and have the data I need! As you may have noticed, I took the bytes needed and marshaled that using AllocHGlobal which gave me a return value of an intptr that will be supplied within the method. This tells where the method should write the bytes needed so I can pull this back later as an object. Speaking of that…

If ($Return) {                    
    $tempPointer = $Buffer
    For ($i=0;$i -lt $ServicesReturned;$i++) {
        #Write-Progress -Status 'Gathering Services' -PercentComplete (($i/$ServicesReturned)*100) -Activity "Pointer: $tempPointer"
        If ([intptr]::Size -eq 8) {
            # 64 bit
            $Object = ([System.Runtime.InteropServices.Marshal]::PtrToStructure($tempPointer,[type][ENUM_SERVICE_STATUS_PROCESS]))
            [intptr]$tempPointer = $tempPointer.ToInt64() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][ENUM_SERVICE_STATUS_PROCESS])
        } 
        Else {
            #32 bit
            $Object = ([System.Runtime.InteropServices.Marshal]::PtrToStructure($tempPointer,[type][ENUM_SERVICE_STATUS_PROCESS]))
            [intptr]$tempPointer = $tempPointer.ToInt32() + [System.Runtime.InteropServices.Marshal]::SizeOf([type][ENUM_SERVICE_STATUS_PROCESS])
        }

Assuming this works as intended, I can then begin processing the data that has been returned. First thing to do is create another copy of the buffer and then I can use that to process the data. The next part had me stumped for a little while because while I could get the data of only one object, I could not figure out why I wasn’t seeing anything else. If you look at the $ServicesReturned value, it had the exact count of services that I should be expecting to see returned.

I used the number of services returned in a For loop and then began processing each item and pulling the object out from the location in memory defined by the intptr and then worked to get the next location by, depending on the current architecture of being 32 or 64bit converting the intptr to either int32 or int64 and then adding that to the size of the ENUM_SERVICE_STATUS_PROCESS object (which will have a size difference depending on if it is 32 or 64 bit. Once that is done, the value is saved to the temp buffer location and the process repeats itself until we are completely through the services.

This process is important because it has to be done another time when we want to get the ENUM_SERVICE_STATUS information.

At the end of all of this, I configured some formatting of the type data so every single property is not shown, but instead what you would typically see when using Get-Service with an added bonus of the StartMode being displayed.

#region Type Display Formatting
Update-TypeData -TypeName System.Service -Force -DefaultDisplayPropertySet State, Name, 
DisplayName, StartMode
#endregion Type Display Formatting

Because I like having some sort of tab completion with the Name parameter, I also configured a custom completer for that parameter. Take note that there will be a slight delay initially because it has to make the query to pull in all of the services to allow the auto completion.

#region Custom Argument Completors
#region Service Name
$completion_ServiceName = {
    param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameter)

    Get-Service2 -ServiceType All | Sort-Object -Property Name | Where-Object { $_.Name -like "$wordToComplete*" } |ForEach-Object {
        New-Object System.Management.Automation.CompletionResult $_.Name, $_.Name, 'ParameterValue', ('{0} ({1})' -f $_.Description, $_.ID) 
    }
}
#endregion Service Name
If (-not $global:options) { 
    $global:options = @{
        CustomArgumentCompleters = @{}
        NativeArgumentCompleters = @{}
    }
}
$global:options['CustomArgumentCompleters']['Get-Service2:Name'] = $completion_ServiceName

$function:tabexpansion2 = $function:tabexpansion2 -replace 'End\r\n{','End { if ($null -ne $options) { $options += $global:options} else {$options = $global:options}'
#endregion Custom Argument Completors

Demo Time

So with that, I wanted to show what this actually does and what the results look like.

Get-Service2

image

A closer look at a service:

Get-Service2 -Name WebClient | Select-Object -Property *

image

As you can see, we can look at the triggers as well as the failure actions for a service.

We can also look at file system drivers:

Get-Service2 -ServiceType FileSystemDriver

image

Same for kernel drivers:

Get-Service2 -ServiceType KernelDriver

image

So is this faster than using traditional methods such as Get-Service or Get-WmiObject –Class Win32_Service? Nope. As of right now, this is the slowest approach but I am working on looking at optimizations to try and knock it down a bit.

image

So in this case, the trade-off for more information is definitely the speed of the function. Regardless, I invite you to download it from the link below and let me know what you think! This function not only works locally, but also against remote systems as well.

Enjoy!

 

Download Get-Service2

https://gallery.technet.microsoft.com/scriptcenter/Robust-Get-Service-f3575557

Posted in powershell | Tagged , , , | 3 Comments

Building a Enum that Supports Bit Fields in PowerShell

I was working on a project recently that required me to have an Enum that allowed bit fields as opposed to the normal fields that we might deal with in our day to day endeavors with PowerShell.

If you aren’t quite sure what I mean by this, here is a quick example that shows the difference between the two.

First, the regular Enum that we are used to:

([DayOfWeek]0..6).ForEach({[pscustomobject]@{Number=$_;Data=[DayOfWeek]$_}})

image

Notice that each number matches up to a single value: 0 = Sunday, 3 = Wednesday and so on.

Now for an Enum with bit fields:

([System.IO.FileAttributes]1..7).ForEach({
[pscustomobject]@{Number=$_;Data=[System.IO.FileAttributes]$_}
})

image

You can see here that depending on the value, you may end up for more than 1 item. This typically will follow a 1,2,4,8,16…etc… approach for the single items and then they perform a bitwise XOR to combine values for more items.

Now that we have covered this topic in a very basic form, we still need to understand how to build one so we can use it in PowerShell. In PowerShell V2..4 we would have to settle on creating the enum by creating a C# here string and loading it up into the session like so:

Add-Type -TypeDefinition @"
[System.Flags]
public enum Animals
{
Dog = 1,
Cat = 2,
Bird = 4,
Lizard = 8
}
"@

All we have to do is ensure that the [System.Flags] attribute is used within our code.

Now we can verify that this actually worked like we wanted:

(1..15).ForEach{[pscustomobject]@{Number=$_;Animal=[Animals]$_}}

image

I’d say it passes the test!

Next up is building one dynamically using reflection. A little more complex but not something that we can’t do..right?

#region Module Builder
$Domain = [AppDomain]::CurrentDomain
$DynAssembly = New-Object System.Reflection.AssemblyName('Random')
$AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
$ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('Random', $False)
#endregion Module Builder

#region Animals
$EnumBuilder = $ModuleBuilder.DefineEnum('Animals', 'Public', [int32])
[void]$EnumBuilder.DefineLiteral('Dog', [int32] 0x00000001)
[void]$EnumBuilder.DefineLiteral('Cat', [int32] 0x00000002)
[void]$EnumBuilder.DefineLiteral('Bird', [int32] 0x00000004)
[void]$EnumBuilder.DefineLiteral('Lizard', [int32] 0x00000008)
$EnumBuilder.SetCustomAttribute(
[FlagsAttribute].GetConstructor([Type]::EmptyTypes),
@()
)
[void]$EnumBuilder.CreateType()
#endregion Animals

The trick here is to create a custom attribute where we supply the default constructor for the Flags.Attribute type add that attribute to the enum.

We can test using the same code to see if everything matches up:

image

Success! Ok, so I have covered the old ways of doing it and even the reflection approach is still valid in V5 if your goal is keeping everything in memory and not writing to disk.

If you are interested in an even easier way of doing this, then check out fellow MVP Matt Graeber’s PSReflect module that gives you the PSEnum function that makes this whole thing a snap!

$Mod = New-InMemoryModule -ModuleName Win32
psenum -FullName Animals -Type Int32 -Bitfield -Module $Mod -EnumElements @{
Dog = 1
Cat = 2
Bird = 4
Lizard = 8
}

If you run the usual code, you can easily see that it works!

In PowerShell V5, we are greeted with an easier way to natively build an Enum in PowerShell with the…Enum keyword! But we don’t quite care about that as we really need to have support for Flags so we can have our bit field support.

Luckily we have that available albeit not documented anywhere yet. We just have to define that it will have the Flags attribute before calling the Enum keyword and we are ready to go!

[Flags()] enum Animals {
Dog = 1
Cat = 2
Bird = 4
Lizard = 8
}

How simple was that? We just create the enum and it is ready to go with not much code or having to compile C# (not that it was that tough anyways) and we can verify once again just to make sure it works like we want it to.

image

No oddness here! It worked like a champ and we have yet another way to accomplish our goal! Just remember that this approach only works with PowerShell V5.

Enjoy!

Posted in powershell | Tagged , , , , | 4 Comments

Another Way to Get Output From a PowerShell Runspace

Most of you who work with runspaces in PowerShell are familiar with the most common way to pull out data that is waiting in another runspace by simply calling Invoke() on the PowerShell instance or using EndInvoke(<handle>) to stop one that is running in the background. Doing either of these will instantly bring back the output that is waiting on the end of that runspace.

image

There is actually yet another way to kick off a runspace in the background and then get the data back without having to call the EndInvoke() method in the PowerShell instance if you are running these asynchronously. It requires just a little bit more work to build out an object to support this but provides the data that you are expecting. Of course, you still need to properly dispose of your runspace when you are done to prevent potential memory leaks from occurring in your console/ISE.

To highlight which one I will be using with BeginInvoke(), you can find it in the image below and I will explain the process after the image.

image

To do this we will begin setting up our runspace in the usual manner by creating the PowerShell instance and adding a script that will run to create an object within it.

$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript({
    [pscustomobject]@{
        Test = 'test'
    }
})

Ok, nothing really new here yet, but now we need to build a System.Management.Automation.PSDataCollection[T] object that will be used both as an input object but also an output object as shown in the method overloads. The TInput is merely saying that we need to provide some sort of object type that goes with the PSDataCollection object that is being built in which this type of collection is called a generic collection as it is strongly typed and will only allow use to store types that are specified to this collection. Now that we have covered that topic, let’s build this out.

$Object = New-Object 'System.Management.Automation.PSDataCollection[psobject]'

image

All that we have left to do now is to call BeginInvoke() and supplying this object twice as the Input/Output and then after the command has finished, check out the results.

$Handle = $PowerShell.BeginInvoke($Object,$Object)

Instead of calling EndInvoke(), I can just view the $Object variable instead and see the output.

image

As I mentioned earlier, even though I have my results, I still need to clean up after myself by disposing of the PowerShell instance that will then close down the runspace.

$PowerShell.Dispose()

And like that, I have another way to gather my data from a runspace!

Posted in powershell | Tagged , , | 4 Comments