Tools of the trade

In many IT focused community websites, the topic of tools comes up quite often.  Rather than replying with a new or modified list each time, I’m keeping an updated list here.

Disclaimers:

  • I’m a ‘Systems Engineer’ in a Microsoft and VMware environment – this list will focus mostly on tools found in these environments.
  • This page was inspired by Scott Hanselman’s list of tools, and will include a good deal of overlap.  His list is a bit more comprehensive and the comments include other suggestions; take a look!
  • This isn’t a comprehensive list.  This is a list of tools I regularly use and find invaluable.

IT tools:

  • PowerShell – Version 3.0, using the ISE.  From my perspective, anyone working in IT supporting a Windows environment should already know or start learning PowerShell for their own and their employer’s benefit.
  • SysInternals – Be sure to check out the entire SysInternals suite, and consider keeping it up to date with a quick script pulling from hereProcess Monitor (procmon) and Process Explorer (procexp) get the most use, followed by PsExec and AutoRuns.
  • Terminals – A great open source RDP (and other) manager.  There are other options, none have persuaded me to leave Terminals.
  • Performance Monitor (perfmon) – Sooner or later you will need to use it.  Even if you implement Operations Manager or another monitoring system that watches performance counters, you will likely need to dive into counters not covered by those systems.
  • Resource Monitor (resmon) – More robust than task manager, not quite as daunting as Process Explorer.
  • WinDirStat – colorful breakdown showing you what is taking up space.  As of May 2013, the last update was November 2011.
  • SpaceSniffer – Less colorful breakdown showing you what is taking up space.  Keeps up as you make changes.
  • Log Parser and Log Parser Studio – Query data from various logs
  • EventCombMT – I usually stick to PowerShell, but this can come in handy every so often when delving into Event Logs.
  • Microsoft Management Console (mmc) – I load mine up with Active Directory Sites and Services, Active Directory Domains and Trusts, Active Directory Users and Computers, ADSI Edit, Certificates, DFS Management, DNS, Group Policy Management, Hyper-V Manager, IIS. Also, Computer Management, Event Viewer, Share and Storage Management, Shared Folders, and Task Scheduler. There is some overlap, which can be helpful in multitasking.
  • Client Hyper-V – We’re a VMware shop, but for quick tests and other use on the desktop, having a built in hypervisor with PowerShell support is invaluable.
  • PowerCLI – Hyper-V isn’t the only hypervisor with great PowerShell support.
  • Operations Manager – If you are licensed for System Center and have a Microsoft ecosystem, OpsMgr is a great way to monitor this.  Also look into the rest of the bundle (ConfigMgr, Orchestrator, etc.)
  • RegScanner – Ever get tired of rapidly pressing f3 in regedit?  This tool quickly scans the registry and simply shows you a list of all matches.  Various options for filtering included!
  • WireShark (formerly Ethereal) – Packet capture and analysis
  • Chart Controls – Build charts with .NET.  You can integrate this with your PowerShell functions or scripts.  If you are a non-profit or could justify the cost, Highcharts are a little prettier and more interactive, but slightly less PowerShell friendly.
  • Notepad2 – I’ve completely replaced notepad with this.  All text files are associated with it, and the alias ‘n’ opens it from PowerShell.  Scott’s blog has some other options, this was a good balance between functionality and light weight.  Some co-workers swear by Notepad++
  • The Practice of System and Network Administration – If there were one book everyone working in or with IT should read, this would be it.  It’s ancient in this line of work (2007), but still holds true.  As of 2012 the third edition was unannounced and in the ‘planning stages

General tools

  • Mouse without Borders – Control up to four Windows computers with one mouse and keyboard, including copying and pasting text.  This changed the way I work.  It’s also quite handy for controlling a presentation PC from the back of a room, or an HTPC from the couch.  The license used to be a bit ambiguous – not any more, you are free to use this at work!
  • OneNote – Great for managing notes and other content collaboratively or for myself.
  • RSS aggregator (Google Reader) – paired with Nextgen Reader on WP8 or Reeder on iPhone. Easiest way to filter through the various sites pertinent to my job, and for personal use. Google dropping this service and campaigning against Windows Phone have lead me to move towards dropping their services for Microsoft and other competitors.
  • 7 Zip – integrates with Windows, wide compatibility, free, command line you can integrate into your PowerShell functions or scripts.
  • SkyDrive – There are many cloud storage options, find one that works for you.
  • SSD – not a tool per se, but opens up many options.  Lighter, less power use, no issues being bumped, fast seek time (no heads to move across a spindle), more IOPS than most RAID setups.  If you plan on running a hypervisor, this is essential.  All of my work and home computers have an SSD.  Well worth cost.

Other helpful lists

That’s it for now.  Do you have any favorite tools?  Feel free to comment!

Get-ntfsAccess

I occasionally find myself needing to determine which folders or files a user or group does or does not have access to.  With nested security groups things can get even more tricky.

I ended up writing get-ntfsAccess, a quick function that will do this for me.  I feed in one or more paths to test, one more users/groups, and I get back either all the folders they have access to, or all the folders they don’t have access to.  The function does the following:

  1. If specified, define and run Get-NestedGroups on the provided entities to provide all nested security groups
  2. Check ACL of each path for any ACE matching the users/groups or their nested security groups

Define and run Get-NestedGroups

This function is a bit longer than the Get-ntfsGroups function it is defined in.

$ADObjectDetails = Get-ADObject -filter "samAccountName -eq '$ADObject'" -Properties memberOf

Get the memberOf property for the object – we need this to determine if it’s a group or another object.

$ADObjectDetails | select -expand memberof | foreach {
     $subGroups += Get-adgroup $_ | select -ExpandProperty samaccountname
}

If it isn’t a group, we want to know what security groups provide access.  Loop through memberOf and get the samAccountName for each of these groups

if($group){  $subGroups = ( Get-ADGroupMember -Identity $ADObject | where {$_.objectClass -like "group"} ).samAccountName  }

If it is a group, we only care what groups are inside the security group

#If there are sub groups, recurse through them
if($subGroups){

    #add results of current query
    $allNestedGroups = [string[]]$subGroups

    #initialize subSubgroupscollection
    $subSubGroupsCollection = [string[]]""

    #look for subsubgroups in each sub group
    foreach($subgroup in $subgroups){
        #Run query, return query results for verbose output, then add it to a collection
        $subSubGroups = Get-NestedGroups -ADObject $subgroup -nestLevel $nestLevel
        if($subSubGroups){ write-verbose "$tabs Level $nestLevel $subgroup returned the following groups:`n$( $subSubGroups | out-string)" }
        $subSubGroupsCollection += $subSubGroups
    }

    #add results from subgroups only if they aren't blank or already included
    $allNestedGroups += $subSubGroupsCollection | ?{$_ -and $allNestedGroups -notContains $_}

}

else{
    #If we hit an empty group, return with nothing
    Return
}

#Once we've recursed through all groups, return results
Write-Verbose "$tabs Level $nestLevel Returning from $ADObject with following nestedGroups:`n$( $allNestedGroups | Out-String)"
$allNestedGroups

After this, we add each group in subGroups to allNestedGroups, and recursively call this function on each of those groups

Check out the innards of Get-ntfsAccess on Script Center for the full definition of the function.

Check ACL of each path for any matching ACE

#Loop through each folder
foreach($folder in $folderlist){

    #Get ACL for the folder
    write-verbose "Checking access for $folder"
    $accessList = (get-acl $folder).access

    #Set access to null - used for determining noAccess
    $access = $null

    #For each access item in the ACL
    foreach($accessItem in $accessList){

        #Identify the group and track overall access for -noaccess parameter
        $accessItemGroup = $accessItem.IdentityReference.Value

        #loop through groups we are searching for
        foreach($group in $entity)
        {
            #if we match a group...
            if($accessItemGroup -like $group)
            {
                #add it to a list, if listOnly or noAccess
                if($listOnly)
                {
                    #add the result unless $noAccess is specified
                    if(-not $noAccess){ $folderResults += $folder | where { $folderResults -notcontains $_ } }
                    $access = 1
                    write-verbose "Access for $folder"
                }
                #otherwise, add an object with access information to results
                else
                {
                    #add the result unless $noAccess is specified
                    if(-not $noAccess){
                        $folderResults += [pscustomobject] @{
                            Path = $folder;
                            Group = $accessItemGroup;
                            FileSystemRights = $accessItem.FileSystemRights;
                            AccessControlType = $accessItem.AccessControlType;
                            IsInherited = $accessItem.isInherited;
                            InheritanceFlags = $accessItem.InheritanceFlags;
                            PropagationFlags = $accessItem.PropagationFlags
                        }
                    }
                    $access = 1
                    write-verbose "Access for $folder"
                }
            }
        }
    }

    #if we didn't find a group matching input, $access is still $null
    if(-not $access -and $noAccess){
        $folderResults += $folder
        write-verbose "No access for $folder"
    }
}

$folderResults

This is the final piece of the code.  I tried to comment things out to show what’s going on.

We have all the users/groups and folders (or files) to test.  The first stage looks at a single path and gets the ACL.  The next stage looks at each ACE in the ACL to see if it matches the users/groups we are interested in.  When it does, add it to the results (if checking for access) or note that we do have access (if checking for no access).  Once we’ve checked all ACEs in an ACL, if nothing matched the group we note that there is no access (if checking for no access).

The function in action

Pick up the script here and make sure you are running PowerShell 3

I start out with C:\temp:

Everything has default inherited ACEs, apart from one I added for a group nested under VSR VDI PFS:

Perhaps I only want a list of folders the provided entities have access to:

Lastly, the folders that VSR VDI PFS does not have access to (all but one):

Bonus:  You can run this on network shares

My next step is to add a parameter that controls the depth of path recursion.  As it is, I’m using the built in Get-ChildItem -Recurse parameter, but I could see situations where you only care about the first few levels of directories.

On an aside, Raimund Andrée wrote a fantastic module that covers managing permissions beyond get and set-acl – check it out here.

Parallel PowerShell: Part II

I posted on parallelization in PowerShell a short while back.  Check out that post for a number of references that I won’t be including here.

I ran into a few situations where a few threads would freeze, preventing tasks from running after the parallel code runs.  There is likely a more official way to do this, but here is how I implemented a timeout for each thread.

Using Boe Prox’s code, I hacked together Run-Parallel.  It does the following:

  1. Define Get-RunspaceData, a function that loops through runspaces, cleans up if they are done or over their max runtime
  2. Take in the code we will run against various computers
  3. Create a runspace pool
  4. For each computer to run against
    • Create a PowerShell instance with the scriptblock, add the computer name as an argument
    • Add details (Computer, PowerShell instance, start time, etc.) to an array
    • Run Get-RunspaceData
  5. After all computers queued up, run Get-RunspaceData until everything has completed

Let’s step through the code for each of these starting at (2):

Take in the code we will run against various computers

        #If scriptblock is not specified, convert script file to script block.
        if(! $scriptblock){
            [scriptblock]$scriptblock = [scriptblock]::Create($((get-content $scriptfile) | out-string))
        }
        #if scriptblock is specified, add parameter definition to first line
        else{
            $ScriptBlock = $ExecutionContext.InvokeCommand.NewScriptBlock("param(`$_)`r`n" + $Scriptblock.ToString())
        }

In this block, we convert $scriptfile into a scriptblock, otherwise we take in $scriptblock and add param($_) to the first line.  This lets you use the Run-Parallel function like a foreach(){} – you just reference the computer as $_.

Create a runspace pool

        $sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
        $runspacepool = [runspacefactory]::CreateRunspacePool(1, $Throttle, $sessionstate, $Host)
        $runspacepool.Open()

In this block, we create a default session state, an create a runspacepool with this state and the throttle parameter (how many to run at once).

On a side note, if you want certain variables or modules to be available to all sessions, this is where you do it. More details here.  For example (this isn’t included in my function):

#EXAMPLE
$sessionstate = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
$sessionstate.ImportPSModule(“ActiveDirectory”)

For each computer

        ForEach ($Computer in $Computers) {
           #Create the powershell instance and supply the scriptblock with the other parameters
           $powershell = [powershell]::Create().AddScript($ScriptBlock).AddArgument($computer)

           #Add the runspace into the powershell instance
           $powershell.RunspacePool = $runspacepool

           #Create a temporary collection for each runspace
           $temp = "" | Select-Object PowerShell,Runspace,Computer,StartTime
           $temp.Computer = $Computer
           $temp.PowerShell = $powershell
           $temp.StartTime = get-date

           #Save the handle output when calling BeginInvoke() that will be used later to end the runspace
           $temp.Runspace = $powershell.BeginInvoke()
           Write-Verbose ("Adding {0} collection" -f $temp.Computer)
           $runspaces.Add($temp) | Out-Null

           Write-Verbose ("Checking status of runspace jobs")
           Get-RunspaceData @runspacehash
        }

In this block, we loop through each computer.

We create the powershell instance (if desired, you can add more arguments if needed by tacking on another .addargument() ), and add it to the pool.

We create $temp, which contains the computer, powershell instance, start time, and handle output from beginInvoke.  We add this object to the $runspaces array that will be used for tracking each runspace.

Finally, we run Get-RunspaceData.  We do this for each computer because we may start getting results before we can build up all the runspaces.

Get-RunspaceData

        Function Get-RunspaceData {
            [cmdletbinding()]
            param(
                [switch]$Wait
            )

            Do {
                #set more to false
                $more = $false

                Write-Progress  -Activity "Running Query"`
                    -Status "Starting threads"`
                    -CurrentOperation "$count threads created - $($runspaces.count) threads open"`
                    -PercentComplete (($totalcount - $runspaces.count) / $totalcount * 100)

                #run through each runspace.
                Foreach($runspace in $runspaces) {

                    $runtime = (get-date) - $runspace.startTime
                    #If runspace completed, end invoke, dispose, recycle, counter++
                    If ($runspace.Runspace.isCompleted) {
                        $runspace.powershell.EndInvoke($runspace.Runspace)
                        $runspace.powershell.dispose()
                        $runspace.Runspace = $null
                        $runspace.powershell = $null
                    }

                    #If runtime exceeds max, dispose the runspace
                    ElseIf ( ( (get-date) - $runspace.startTime ).totalMinutes -gt $maxRunTime) {
                        $runspace.powershell.dispose()
                        $runspace.Runspace = $null
                        $runspace.powershell = $null
                    }

                    #If runspace isn't null set more to true
                    ElseIf ($runspace.Runspace -ne $null) {
                        $more = $true
                    }
                }

                #After looping through runspaces, if more and wait, sleep
                If ($more -AND $PSBoundParameters['Wait']) {
                    Start-Sleep -Milliseconds $SleepTimer
                }

                #Clean out unused runspace jobs
                $temphash = $runspaces.clone()
                $temphash | Where {
                    $_.runspace -eq $Null
                } | ForEach {
                    Write-Verbose ("Removing {0}" -f $_.computer)
                    $Runspaces.remove($_)
                }

            #Stop this loop only when $more if false and wait
            } while ($more -AND $PSBoundParameters['Wait'])

        #End of runspace function
        }

So, now we have $runspaces, an array holding information on all the parallel threads we will be running. When we run Get-RunspaceData, the function loops through $runspaces to check if each runspace has completed or has over run the maxRunTime we specified, cleaning up as necessary

Run Get-RunspaceData until everything has completed

        $runspacehash.Wait = $true
        Get-RunspaceData @runspacehash

We’re at the end! $runspaces contains all (remaining) threads that will need to run. Here, we simply add the -wait parameter for Get-RunspaceData. When this is specified, the function keeps looping until $runspaces is empty.

Closing

That’s about it!  The full script is available here in the Script Repository.  I also added ForEach-Parallel – this adds the same runtime tracking to Tome Tanasovski’s version of Foreach-Parallel.

If anyone has any suggestions on revising this, please let me know!  I assume there would be computational overhead, as get-date runs for every single runspace when looping through $runspacepools.  When you’re querying thousands of computers, this might not the most efficient way to implement timeouts on a runspace.

Update

The method I use to track start time is not accurate.  Runspaces are all defined initially, and queued up in the runspacepool.  This means we could record a starttime, the runspace could wait for a long time if the queue is full, and the timeout would expire the runspace immediately as the starttime was defined at the start.

I changed the default Timeout to 0.  If you do want to use this parameter, set it for roughly the duration you expect all threads will take to complete, and it will provide a simple fail-safe against hung threads.

Boe Prox suggested using nested runspaces – create an intermediary runspace that tracks the runtime of the real runspace.  It sets the timeout accurately, but falls short in performance.  Given that the motivation behind these commands is performance, I won’t be using this method.

I added a ‘maxQueue’ control that will ensure the runspacepool is not filled up immediately.  It is defined as ($throttle * 2) + 1, to help ensure the runspacepool always has at least ($throttle + 1) runspaces queued up and ready to run.  This can be changed depending on whether you prefer an accurate timeout or performance.

Get-VolatileInfo – Quick Troubleshooting Info

When I run into an unknown issue with one or more workstations or servers, I often run through the same set of steps.  Eventually, I find a tidbit of information that ideally identifies the root cause of the issue, or at worst points me in a direction to find more information.

I decided to script out this basic first step.  Get-VolatileInfo will run a number of queries against one or more remote computers.  It returns the info it collects in global variables you can then manipulate to find what you need; alternatively, it provides searchable sortable HTM files thanks to a method I borrowed from Douglas Finke.

Head over to the Script Center repository for the function code or ps1 file.  This link includes details on installation and dependencies.  Once you have the function loaded, Get-Help Get-VolatileInfo –Full will provide help with parameters and examples.

How does it work?

At a high level, I define dependency functions, run through each computer listed to collect certain info, and read that information.  Here’s what it might look like if I heard there was trouble with c-is-hyperv-1:

image

I now have a number of variables populated with troubleshooting information for c-is-hyperv-1.  The text below tells me the command I can run to list these again, or I could pipe the command to remove-variable and get rid of the variables.  An explorer windows also popped up with the HTM files created due to the –View command.

Perhaps I want to see what server lsass.exe is talking with.  I pull up the c-is-hyperv-1netstat.htm file and search for lsass:

image

Alternatively, I can do the same via PowerShell (Note:  I’m enclosing the variable name in curly brackets as it has dashes in it – tab completion will do this for you!):

image

What information does this provide?

Here’s what I’m collecting thus far, all of which is in object form (or in the handy HTM files):

  • AutoRuns: This is a full list of everything from AutoRunsC.exe.  You can sort or filter by category to weed out anything you don’t need.  It shows a comprehensive list of items that auto start or affect auto start.
  • ComputerInfo: A bunch of info from WMI – hostname, OS, SP, CPU, RAM, Free Space for C:\, last reboot time, system time, and the difference between system time and the system time from the computer you ran the query on.
  • CurrentUsers:  Users from win32_loggedonuser
  • InstalledSoftware:  Software listed in the registry
  • LogApp: -eventCount events from Application event log (Default is 250)
  • LogSec: -eventCount events from Security event log (Default is 250)
  • LogSys: -eventCount events from System event log (Default is 250)
  • NetStat: From Get-NetworkStatistics – Netstat –ano results with hostnames and process names resolved, in object form
  • NetworkAdapter#:  A variety of configuration info for each IPenabled network adapter, starting with NetworkAdapter0, NetworkAdapter1 …
  • OpenFiles:  Info on open files
  • Processes:  Info from Get-Process
  • Profiles:  List of profiles and the date ntuser.dat was last written to.
  • Reboots:  Any instances of eventID 6009 from event logs, indicative of system starting up
  • Services: Info from Get-Service
  • ServicesNotStarted:  Services set to autostart, but not running
  • Shares:  Info on shares

If you have any suggestions on information to collect or not to collect, or any other insight, it would be greatly appreciated!

PowerShell Resources

I’m starting to build a list of PowerShell resources for co-workers and me.  I plan to keep this up to date and add resources on a regular basis, so you may want to check back occasionally.

Cheat sheets and quick references

Blogs and other resources

Communities

Videos and Podcasts

Books and eBooks

Tools and Add-ons

This is what I have so far. Some of it is a bit dated;  for example, PowerGUI isn’t quite as active as it once was.  I’m likely overlooking a number of resources that I simply find using google, or that are out of the scope of what I do (e.g. I don’t work with SharePoint).

If you have any suggestions on resources to include, how to better organize these, or any other insight, it would be greatly appreciated!

New-RemoteProcess

I have been a bit busy at home and work recently.  Will be starting back up on the white box soon!

I recently wrote about Get-NetworkStatistics.  This basically runs netstat on a remote system and parses the results.  Today, we ran into an issue where we wanted to run ‘set’ on a remote system, and the on-site technician could not get to the command prompt due to certain restrictions.  I already had the framework for running a command remotely, so I made a few adjustments to generalize things.

Getting started

Click here for the function code and ps1.  Once you add the function to your session, you can run commands like this:

image

In this example, I ran ‘set’ against localhost and c-is-ts-91l.  I specified the pattern ‘processors’ to filter the results.  The result is an object for each computer with the computer name, output from the command, errors from the command, the command that ran, and the pattern.

image

In this example, I ran ‘ipconfig /all’ and access the results property.

image

In this example, I ran ‘set’, filtered for ‘computername’, and select only the computer and results.

Do keep in mind the results property is an array.

There are certainly other options for this!  Mark Russinovich’s PsExec is always a solid choice, but collecting and perhaps acting upon the output might require a bit of work.  If you have PowerShell remoting set up in your domain and all systems have PowerShell, that might be a better option.

If you run into any issues or have any suggestions, please let me know!

Parallel PowerShell

*Edit:  Updated code is available in this post

There are times where running parallel tasks in PowerShell would be handy.  Unfortunately, while Workflows can run foreach –parallel, there’s no built in function to handle this in PowerShell.  I came across a few tasks where parallelization would save time, and figured it was time to dive into the subject. I ended up with three functions that heavily borrow from the work of Tome Tanasovski, Boe Prox, and Ryan Witschger.

Before we go into these, I would highly recommend you watch Dr. Tobias Weltner’s webcast on speeding up PowerShell. I found it quite insightful on a variety of topics – certainly well worth the quick registration process. There are other helpful resources on parallelization available as well, including these from Jon Boulineau, Will, Joe Pruitt, and Chris O’Prey.

Do keep in mind that several of the authors above warn that you should use them with caution, and that I am not nearly as familiar with the language as these authors : ) That being said, the modifications I made were minimal; for example, adding a non-functional progress bar, adding AD queries, and other non-runspace changes.

What can Runspaces do for me?

A while back I built a query that runs through AD and queries every single computer account, collecting a variety of information and running certain diagnostics. It is resource light and a helpful backup to existing monitoring solutions. Unfortunately, it takes a very long time, as a good number of systems will not respond; those three seconds add up quickly. Running things in parallel cut the entire query from a day or two to 5 hours.

Running more simple tasks like a single test-connection or a couple WMI queries would see an even greater benefit from parallelization. If you check out Boe’s post on this, you will see that for a simple WMI query upon successful test-connection, a 200 system query takes 22 seconds using runspace pools with a throttle of 10. The next fastest method was the standard synchronous foreach at 158 seconds. If you manage many systems, you can see that using runspace pools will be invaluable.

Here’s another real world example.  I pulled the first 100 computer accounts from AD and simply ran test-connection against them.

8 Minutes 42 seconds

Many of these computers no longer exist, but running queries against computers that might not respond is a likely scenario that will delay your commands.  In this case, the performance difference went from 54 seconds using a runspace pool with 10 threads, to 8 minutes and 42 seconds otherwise.

How do I get started?

Go back and make sure you review the posts above that go into actual technical detail. Then download the code from here.  Add the functions to your profile or a custom module, paste them into your session, or dot source them.  Once the functions are available, run get-help –full against them for more details!  The functions are Foreach-Parallel, Run-Parallel, and Run-ParallelJobs.

The Run-Parallel and Run-ParallelJobs functions each include parameters to query AD.  These query the default domain.  To use the QuerySvrAD and QueryWksAD to query server and workstation computer accounts, you must modify the scripts with the appropriate distinguished names of the OU or containers where these accounts could be found.  Ctrl+F and look for ExampleDC to find the lines where this can be set.

I plan to expand on these; for example, progress tacking could be improved, adding parameters to allow more than one argument passed to the parallelized script(block) by stacking AddArgument(), and adding timeout values or resource constraints on runspaces.

If you run into any trouble or have any suggestions, please let me know!

Other stuff

Google posted a bit on their datacenters and invited someone from Wired to visit and write a story on it.  Wouldn’t it be nice!