Dealing with Runspacepool Variable Scope Creep in PowerShell

Something that I had noticed a while back that I would receive some output values in my objects that shouldn’t have been there. Properties that should have been Null had values in them, and not just random values, but values that matched some of the other outputted objects. This was somewhat worrisome because I didn’t want my project, PoshRSJob, to have this same problem (which it did have). Duplicating the issue was simple, and actually fixing ended up being simple but it wasn’t something that immediately stood out to me, mostly because I had thought that I tested out the solution already and it didn’t work.

The solution to this also shows that sometimes the simplest of fixes can solve the more annoying or complex problems as well as showing that sometimes it is ok to take a little time away from an issue (as long as it isn’t critical to something) and coming back to it can help refresh your mind so you can come back more focused on the issue.

My demo code below will run a simple script block and outputs an object with a few properties that show the pipeline number (basically just something to supply as an outside variable into the scriptblock), the ThreadID so I know that the runspacepool is behaving by reusing the existing runspaces and lastly a boolean value that is probably the most important value in that it shows whether the pipeline value (the first item in our object) is either an odd or even number. If it isn’t an odd number, then it should be a null value and if it is odd, then a boolean value of $True is used.

I’m determining whether the value is odd or even by using –BAND which is a Bitwise AND statement.

1..10 | ForEach {
    [pscustomobject]@{
        Number = $_
        IsOdd = [bool]($_ -BAND 1)
    }
}

image

You can see that this accurately shows which values are odd and which values are even.

Now in my demo code I will use the same logic but just won’t show False if it is even. The idea is that there should be an alternating Null/True value for each returned object.

#region RunspacePool Demo
$Parameters = @{}
$RunspacePool = [runspacefactory]::CreateRunspacePool(
    [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
)
[void]$RunspacePool.SetMaxRunspaces(2)
$RunspacePool.Open()
$jobs = New-Object System.Collections.ArrayList
1..10 | ForEach {
    $Parameters.Pipeline = $_
    $PowerShell = [powershell]::Create()
    $PowerShell.RunspacePool = $RunspacePool
    [void]$PowerShell.AddScript({
        Param (
            $Pipeline
        )
        If ($Pipeline -BAND 1) {
            $Fail = $True
        }
        $ThreadID = [System.Threading.Thread]::CurrentThread.ManagedThreadId
        [pscustomobject]@{
            Pipeline = $Pipeline
            Thread = $ThreadID
            IsOdd = $Fail
        }
        #Remove-Variable fail
    })
    [void]$PowerShell.AddParameters($Parameters)
    [void]$jobs.Add((
        [pscustomobject]@{
            PowerShell = $PowerShell
            Handle = $PowerShell.BeginInvoke()
        }
    ))
}
While ($jobs.handle.IsCompleted -eq $False) {
    Write-Host "." -NoNewline
    Start-Sleep -Milliseconds 100
}
## Wait until all jobs completed before running code below
$return = $jobs | ForEach {
    $_.powershell.EndInvoke($_.handle)
    $_.PowerShell.Dispose()
}
$jobs.clear()
$return
#endregion RunspacePool Demo

Running this code shows that this fails rather impressively by showing all but a single returned object as being Odd with the Pipeline property value.

image

Yea, this is not a good thing if you are looking for any sort of accuracy in your returned data. I will note that you can mitigate this by calling the Remove-Variable cmdlet against the $Fail variable at the end of your scriptblock, but this isn’t really something that I would expect everyone to do as a solution should really be available within the module itself or at least some way to avoid handling this within the scriptblock.

I looked at many possible options such as changing the runspace thread options thinking that it was a symptom of reusing the same runspace and thinking that it was “just the way it is” and that users would have to remember to remove the variables at the end of the scriptblock execution. Thankfully, that was not the solution that I was going to need to stick with. I ended up stepping away from this issue for a few months because I just couldn’t figure out what was going on and my attempts to fix it ended in failure. Rather than chase my own tail on this, I decided that I would refocus my efforts on something else and then re-engage this at a later date. By doing this, I was able to think better on what I should be looking at as well as testing my different ideas out.

So the big question is: what is your solution to this issue? Well, it comes down to setting a value of $True on the UseLocalScope property when calling the AddScript() method early into the runspace build.

SNAGHTML1e6ff5e1

By setting this value, now everything behaves as expected and we no longer have the oddness of values being where they shouldn’t be at.

#region RunspacePool Demo
$Parameters = @{}
$RunspacePool = [runspacefactory]::CreateRunspacePool(
    [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
)
[void]$RunspacePool.SetMaxRunspaces(2)
$RunspacePool.Open()
$jobs = New-Object System.Collections.ArrayList
1..10 | ForEach {
    $Parameters.Pipeline = $_
    $PowerShell = [powershell]::Create()
    $PowerShell.RunspacePool = $RunspacePool
    [void]$PowerShell.AddScript({
        Param (
            $Pipeline
        )
        If ($Pipeline -BAND 1) {
            $Fail = $True
        }
        $ThreadID = [System.Threading.Thread]::CurrentThread.ManagedThreadId
        [pscustomobject]@{
            Pipeline = $Pipeline
            Thread = $ThreadID
            Fail = $Fail
        }
        #Remove-Variable fail
    }, $True) #Setting UseLocalScope to $True fixes scope creep with variables in RunspacePool
    [void]$PowerShell.AddParameters($Parameters)
    [void]$jobs.Add((
        [pscustomobject]@{
            PowerShell = $PowerShell
            Handle = $PowerShell.BeginInvoke()
        }
    ))
}
While ($jobs.handle.IsCompleted -eq $False) {
    Write-Host "." -NoNewline
    Start-Sleep -Milliseconds 100
}
## Wait until all jobs completed before running code below
$return = $jobs | ForEach {
    $_.powershell.EndInvoke($_.handle)
    $_.PowerShell.Dispose()
}
$jobs.clear()
$return
#endregion RunspacePool Demo

image

Success! The $Fail variable doesn’t try to sneak across to another runspace that is running to prevent an inaccurate display of what number is odd when it should be treated as an even number by remaining Null.

Update: If you declare a Global variable within the scriptblock ($Global:Fail=$True), it will be treated as though you never wanted to use the UseLocalScope and will persist throughout all of the subsequent runspaces that you run in your runspacepool. Just another reason why you should always avoid using the Global scope unless absolutely needed!

So in the end, what was a pretty serious issue in how the variables and their values were being preserved across runspacepools ended up being a rather simple fix once I came back from a little time away from troubleshooting.

Posted in powershell | Tagged , , , | 6 Comments

2018 PowerShell Resolutions

This As I did last year, and the years before that, I wanted to review my last PowerShell resolutions and see how well I did and then take a look at the coming year and pick a few things that I’d like to strive for either in learning new things with PowerShell or as part of the community. With so much happening in the world of PowerShell, it should be pretty fun to figure out things that I would like to learn and talk about in the coming new year. Also, with a new position as a SQL DBA, I am looking forward to doing some amazing things with PowerShell and SQL.

Without further ado, let’s see what I said last year and whether I met my goal with each resolution. Anything in Green are resolutions that I met while those in Red are those that I missed.

  • Speak at a user group or conference this year.
    • I spoke not only at the Austin PowerShell User Group, but also the Tampa PowerSjhell User Group this year. I didn’t get to speak at a conference this year, but maybe next year.
  • Write at at least 2 blogs that relate to SQL or InfoSec.
  • Start a new PowerShell project.
    • I was able to put together a few different projects that dealt with inventories of both Windows servers and SQL Servers. I had a lot of fun with each of these as they had each had me thinking a different way in what I felt was useful to keep in an inventory. While I haven’t worked much on them lately, there are definitely things that I would like to add to them to make them even more useful to the community.
  • Operational Validation with PowerShell and Pester.
    • While I didn’t write anything regarding this resolution, I did work with Pester off and on through out the year and tried to incorporate some of the Operational Validation in various tests to see how best I could utilize this in my environment. Definitely excited to  share some of my stuff as I do more work with them.

Looking back, I am happy to see that I was able to hit on all of my resolutions and show them as being completed. Of course, completing my resolutions for 2017 doesn’t mean that I am done. With 2018 coming up in the next few hours ( for me at least, those of you may have already celebrated it), it is time to think about what I would like to accomplish next year.

  • Speak at a user group or conference this year.
    • This will always be a common resolution to reach as I like to share my knowledge and projects with anyone who would like to listen Smile
  • More work with SQL
    • Being a SQL DBA, I am looking to continue my work with PowerShell to manage and report on SQL Server as well as making use of the amazing DBATools.
  • Open Source PowerShell Contribution
    • There are definitely some things that I would like to see in PowerShell and now that it is open source, now is the best time to get involved to help make PowerShell even better!
  • Yahoo Fantasy Football Module
    • Yep, this is very specific but something that I have been thinking about doing for a couple of years now. If I call it out specifically to work on, perhaps I can make the move to actually build it.

So those are my resolutions for 2018. Some are community based, some are more professional development driven and another is just outright for run. And the same should be done for you! You don’t have to be focused just on one aspect of the resolution, mix it up and have fun with it while learning something new!

Feel free to share your resolutions below and let me know what you are looking to do in 2018!

Posted in powershell | Tagged , , | 3 Comments

Quick Hits: Getting the Local Computer Name

I’ve had a few emails and questions about the best way to get the hostname of the local computer that you are currently logged into and felt that a quick (and I mean quick Smile) post here would be worthwhile.

Using WMI or the CIM cmdlets, you can pull the information from just about any class as there is the __Server property which gives you the name, or you can make of the PSComputername property (which is just an alias to __Server thanks to a type file). Here is one such example:

Get-WMIObject –Class Win32_Bios | Select PSComputername, __Server

image

A more simple approach is making use of a legacy command, hostname.exe which will present you with the local machine’s computername.

image

There are also environmental variables that you can make use of to get this information like COMPUTERNAME.

$Env:Computername

image

That’s it! Like I said, this was short and sweet. Do you know an other ways to display the hostname of your local machine? Comment with them below!

Posted in powershell | Tagged , , , | Leave a comment

Recent Articles on MCPMag

I’ve been busy working on some articles for MCPMag.com and wanted to share a couple of stubs to my most recent articles.

Gathering Installed Software Using PowerShell

If there is one thing an administrator finds themselves doing, it is probably determining what software is installed on their system. It could be simply for just knowing what they have installed, or determining if some software installed may have vulnerabilities which are fixed via a security update or performing an audit for software (which may not have been approved to be installed). Either way, having a means to locate this software can be difficult if you do not have tools like SCCM or another third-party tool available to perform this type of audit.

PowerShell can help us in gathering the software on a local or remote system by giving us a couple of different options to perform the software gathering. One is through WMI and another is by looking in the registry.

The WMI Approach
I’m going to cover the WMI first only because you should never use it as a means to collect data on installed software. I’m talking about the Win32_Product class in WMI. This class is misused in a number of scripts because while it does provide you the information about the installed software, it comes with a cost associated with it.

To show this, I will perform a WMI lookup for software and then show you what happens as we are receiving data from WMI on installed software from this class.

More of this is available at https://mcpmag.com/articles/2017/07/27/gathering-installed-software-using-powershell.aspx

 

Creating Shares in Windows Using the SmbShare Module in PowerShell

Working with Windows shares, you can easily create a location for others to use to store data using a simple share name that could map several folders down in a server (or client). This allows for better simplicity of gaining access to these resources for all users. For the longest time, the best way to create a share was going through the UI by clicking on a folder, and going through the motions to create a share on the folder and then assign the proper share permissions for the newly created resource.

PowerShell was able to be used as well in this, but you had to have some knowledge of WMI and being able to properly create the share and ensure that you picked the right type of share (disk in this case). This wasn’t exactly user friendly as the method to create the share required an integer to represent the type of share. So if you didn’t know the right number for a disk share (it’s 0), then you might find yourself on the receiving end of errors or looking up the proper number to create the right kind of share. Adding users or groups to the share permissions is another thing all together. Working with the proper access type (yes, more integers to work with here) as well as creating the acceptable trustee format will make you wish for an easier approach to all of this.

More of this is available at https://mcpmag.com/articles/2017/07/13/creating-shares-in-windows-using-the-smbshare-module.aspx

I have more blog content queued up here so stay tuned as I begin working through and publishing the articles and also keep watching MCPMag as I continue to produce some great articles on that site as well.

Posted in powershell | Tagged , , , | Leave a comment

Quick Hits: Finding all Hyperlinks in an Excel Workbook

Recently, I had the need to pull out all of the hyperlinks from an excel worksheet that was given to me. While I could have gone the manual approach by looking through all of the cells in the worksheet and copying all of the hyperlinks out manually, that would have been a long grueling thing to do.

PowerShell fortunately gives me all of the ammo I need by allowing the use of COM object creation to hook into the Excel.Application COM object and then open up an existing Excel document and locate all of the hyperlinks which are in the spreadsheet. Automation will always be better than manual if given the opportunity to use it.

Since this is a ‘Quick Hits’ article, let’s not waste any time and step through the process of locating all of the hyperlinks in an Excel document. The first thing that I will do is create the Excel.Application COM object and then open up my existing Excel document using the Open method under the Workbooks property on the Excel object.

#region Load Excel
$excel = New-Object -ComObject excel.application
$excel.visible = $False
$workbook = $excel.Workbooks.Open('C:\users\proxb\desktop\Links.xlsx')
#endregion Load Excel

Note that you will need to supply the full path name to the document, otherwise the open attempt will fail.

From there, I will reference whichever worksheet that has the links I want to pull. In this case,  I only have a single worksheet so I will use the WorkSheet property and specify ‘1’ which says to use the first worksheet in the workbook. If you wanted the second worksheet,  then you would have used a ‘2’. The worksheet object has a property called Hyperlinks which, as you might guess, lists all of the cells with have hyperlinks in them.

$workbook.Worksheets(1).Hyperlinks

image

As you can see, not only do you get the hyperlinks, but also the display text and the screen tips if you happen to use them.

I want a little more information such as the row and column where each of these hyperlinks reside at. To do this, I will make use of the Range property which contains the cell’s row and column number and add that to my new custom object.

$Hyperlinks = $workbook.Worksheets(1).Hyperlinks
$Hyperlinks | ForEach-Object {
    $Range = $_.Range
    [pscustomobject]@{
        Display = $_.TextToDisplay
        Url = $_.Address
        Screentip = $_.ScreenTip
        Row = $Range.Row
        Column = $Range.Column
    }
}

image

So there you go. We can quickly pull all of the hyperlinks from an Excel spreadsheet using PowerShell and besides displaying the Url, we can even locate exactly where in Excel the hyperlink is at by looking at the row and column location.

Posted in powershell | Tagged , , | 2 Comments