Category Archives: Script Sharing

A collection of helpful PowerShell scripts, functions, and modules.

Active Directory User Lookup Form

Download link at bottom of post.

I had some free time recently, so I decided I would write my first, hand-coded PowerShell form using Windows Forms. It took a bit to get started and feel comfortable, but before I knew it, I was adding some useful things to my form. My form’s purpose is to lookup users in Active Directory by their user logon name (think, SamAccountName) and return a specific set of properties — Name, Distinguished Name, Mail, Title, Department, and Office Phone.

Feel free to download, and use the form if you think it may be helpful. While there are a few things I’d like to add, and change, I think it’s a solid, version 1.0 effort. There’s a screen capture of the form in action below, and the download link is just beneath that.

In closing, I wouldn’t be surprised to find out that a tool, such as this, has already been developed and made available — I didn’t bother checking for that prior to writing this tool. I just wanted to completely write my own form in PowerShell with Windows Forms, and needed an idea.

Script Sharing - PowerShell Active Directory User Lookup Form01

Download the Active Directory User Lookup Form here: https://gist.github.com/tommymaynard/b833e7fa33dd76f2484b73db58a7d281

Return File Sizes in Bytes, KBs, MBs, and GBs, at the Same Time

I scanned a recent forum post on PowerShell.org where the user seemed (again, I scanned it) to want to run some files through Get-ChildItem and Select-Object, and report the size in something other than the bytes default. It didn’t appear he, or she, wanted all the files in one of the measurement groups: B, KB, MB, or GB, but instead in their proper measurement group, dependent on the file’s size.

That may be hard to understand at first read, so let me try and explain it another way. If a file has less than 1,024 bytes, then it should be reported in bytes, if it has 1,024 – 1,048,575 bytes, then it should be reported in Kilobytes, if it has 1,048,576 – 1,073,741,824 bytes then it should be reported Megabytes, and if it has 1,073,741,825 or more bytes, then it should be reported in Gigabytes.

I wrote a long, “one-liner” to do this and have included it, and some sample output, below. I don’t profess to guarantee this doesn’t have any errors, so if you find some, then let me know. In addition, it shows a nice example of a switch statement inside the Expression portion of a calculated property. Fancy.

Get-ChildItem -Path 'C:\test' |
    Select-Object Name,
        @{L='Size';E={
                switch ($_.Length) {
                    # Bytes
                    {$_ -eq 0} {"$('{0:N2}' -f $_) bytes"; break}
                    {$_ -eq 1} {"$('{0:N2}' -f $_) byte"; break}
                    {($_ -gt 1) -and ($_ -le 1023)} {"$('{0:N2}' -f $_) bytes"; break}

                    # Kilobytes
                    {$_ -eq 1024} {"$('{0:N2}' -f ($_/1KB)) Kilobyte"; break}
                    {($_ -gt 1024) -and ($_ -le 1048575)} {"$('{0:N2}' -f ($_/1KB)) Kilobytes"; break}

                    # Megabytes
                    {$_ -eq 1048576} {"$('{0:N2}' -f ($_/1MB)) Megabyte"; break}
                    {($_ -gt 1048576) -and ($_ -le 1073741824)} {"$('{0:N2}' -f ($_/1MB)) Megabytes"; break}

                    # Gigabytes
                    {$_ -eq 1073741825} {"$('{0:N2}' -f ($_/1GB)) Gigabyte"; break}
                    {$_ -gt 1073741825} {"$('{0:N2}' -f ($_/1GB)) Gigabytes"; break}

                    default {Write-Warning -Message 'Unknown Error.'}
                }
            }
        } | Format-Table -AutoSize

Name           Size
----           ----
My-A-File.txt  1.00 byte
My-A-File2.txt 445.00 bytes
My-B-File.txt  1.00 Kilobyte
My-B-File2.txt 1.30 Kilobytes
My-C-File.txt  2.09 Megabytes
My-D-File.txt  3.59 Gigabytes

I can easily see how someone might want to remove the singular vs. plural: Kilobyte vs. Kilobytes, and just use B, KB, MB, and GB, and so…

Update: I gave this some more thought today, and I really didn’t feel like it was okay to not include a modified version that uses abbreviations (B, KB, MB, GB) instead of the full words, as in the previous example. It has removed some complexity, as well, as you can see below.

Get-ChildItem -Path 'C:\test' |
    Select-Object Name,
        @{L='Size';E={
                switch ($_.Length) {
                    # Bytes
                    {($_ -ge 0) -and ($_ -le 1023)} {"$('{0:N2}' -f $_) B"; break}

                    # Kilobytes
                    {($_ -ge 1024) -and ($_ -le 1048575)} {"$('{0:N2}' -f ($_/1KB)) KB"; break}

                    # Megabytes
                    {($_ -ge 1048576) -and ($_ -le 1073741824)} {"$('{0:N2}' -f ($_/1MB)) MB"; break}

                    # Gigabytes
                    {$_ -ge 1073741825} {"$('{0:N2}' -f ($_/1GB)) GB"; break}

                    default {Write-Warning -Message 'Unknown Error.'}
                }
            }
        } | Format-Table -AutoSize

Name           Size
----           ----
My-A-File.txt  1.00 B
My-A-File2.txt 445.00 B
My-B-File.txt  1.00 KB
My-B-File2.txt 1.30 KB
My-C-File.txt  2.09 MB
My-D-File.txt  3.59 GB

Find DNS Servers Being Used by DHCP Scopes

Download the Get-TMDhcpDNS function here: https://gist.github.com/tommymaynard/afdb78038e8639d5d23baaaaf897cac1

There was a Microsoft TechNet forum post last week regarding trying to obtain the DNS servers being used by different DHCP Scopes (Here’s the link: https://social.technet.microsoft.com/Forums/en-US/58723d31-7586-40c3-acd2-183f20b49daf/how-to-dump-the-dns-options-for-each-dhcp-scope?forum=ITCG).

I originally thought the person wanted to get the DNS Servers listed in the Server Options (what the scopes use by default), until he (or she) better clued me in to wanting the DNS Servers listed in the Scope Options, when there was one. At times there won’t be any DNS Servers listed in the Scope Options, and it’ll use the Server Options instead.

Since that forum post, I wrapped up a few commands to create a full-service advanced function. Get-TMDhcpDNS function will collect the Scope Name, Scope ID, DNS Servers, and whether the DNS Servers assigned, are done so in the Scope Options or Server Options.

If you think this might be helpful for you or someone else, then please download it, test it, and rate it. Thanks, and here’s an example of the function in action:

PS C:\> Get-TMDhcpDNS -ComputerName 'dhcpsrv1.mydomain.com' | Format-Table -AutoSize

Name                  ScopeName  ScopeID     DNS                                    ScopeOrServerDNS
----                  ---------  -------     ---                                    ----------------
dhcpsrv1.mydomain.com Building01 10.10.10.0  10.10.10.10.20,10.10.10.21,10.10.10.22 Scope
dhcpsrv1.mydomain.com Building02 172.16.16.0 172.16.16.5,172.16.16.15               Server

Use the link above to download the function.

Update: There was an issue with the DNS Server (System.Object[]) when piping the function to Export-Csv. That’s been corrected in 1.0.2. Here’s a post I had to reference (again): http://learn-powershell.net/2014/01/24/avoiding-system-object-or-similar-output-when-using-export-csv/.

An Improved Measure-Command: Multiple Commands, Multiple Repetitions, Calculated Averages, and Pauses Between Runs

Download the Measure-TMCommand function here: https://gist.github.com/tommymaynard/c97c5248d76aba08f1c8aa01096aa12b

In Windows PowerShell, there are often several ways to complete the same task. With that in mind, it makes sense that we might want to determine how long commands and scripts take to complete. Until now, Measure-Command has been the cmdlet we’ve used.

While Measure-Command has been helpful, I’ve often thought it should include some additional functionality. Therefore, I’ve written an advanced function, Measure-TMCommand, that adds all the benefits listed below:

– Continually measure the execution time of a single command and/or script, up to a user-defined number of repetitions.

– Continually measure the execution time of multiple commands and/or scripts, up to a user-defined number of repetitions.

– Calculate the average time a command(s), and/or a script(s)  takes to execute.

– Display limited hardware information about the computer where the command and/or script is being measured.

– Includes an option to display the output of the command and/or script, as well as the measurement results.

Updated 4/15/2015 (v1.2.1): Added a parameter -TimeInBetweenSeconds with a parameter alias of -Pause. This will pause the function between executions, allowing the ability to test at different times between a set time. For instance, let’s say you want to measure a command every 1/2 hour for six hours: 12 repetitions with 30-minute pauses. You would then run the command with the -Repetitions parameter with a value of 12 and the -TimeInBetweenSeconds (or -Pause) with a value of 1800 (as in 1800 seconds, or 30 minutes).

Here’s the function in action:

Measure-TMCommand1.2.1

In the example, above, we can easily determine that using the -Name parameter of the Get-Service cmdlet is faster than piping the entire result set to the Where-Object cmdlet, and then filtering on the name. Notice that not all properties were returned — only the ones in which I was interested.

With the addition of the -TimeBetweenInSeconds, or -Pause, parameter I have considered that this function might be better served to also have an -AsJob parameter. I’ll look into it, but no promises. Thanks, and enjoy.

Script Sharing – Determine the Node to GUID Mapping

If you’ve been following my recent posts, you know that the PowerShell Summit North America 2015 is only days away. I’ve used April to learn (as much as I can) about DSC. I wasn’t completely new to it — I’ve been following along for a bit — but there is still plenty I didn’t know, or at least haven’t experienced hands-on. Anyway, I’m doing whatever I can to absorb as much DSC knowledge as possible, before next week.

While I only have a single target node at this point, I stopped and wondered how obnoxious it may be to determine the GUID to node mapping, when I update a configuration script. I know I can get it from the target node, by using Get-DscLocalConfigurationManager, but what’s an easier way to get them all, at once? While I could query all the nodes, I figured I can also query my MOFs’ directory, providing we trust that source, and why shouldn’t we.

I wrote out a quick and dirty function that I’ve included below. Point this function at your MOFs’ directory on your DSC Pull Server and ta-da, it’ll create a PSCustomObject with your node names and matching GUIDs.

Disclaimer: I’m still learning DSC and may one day realize this function was a waste of time.

Function Get-TMDSCGuid {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true)]
        [string]$Path
    )

    Begin {
        Write-Verbose -Message 'Collecting MOF files.'
        try {
            $Files = Get-ChildItem -Path $Path -Filter '*.mof' -ErrorAction Stop | Select-Object -Property *
        } catch [System.Management.Automation.ItemNotFoundException] {
            Write-Warning -Message "This path does not exist: $Path"
        }
    } # End Begin.

    Process {
        If ($Files) {
            Write-Verbose -Message 'Checking MOF files.'
            Write-Verbose -Message 'Writing ComputerName-GUID Mappings.'
            ForEach ($File in $Files) {
                $ComputerName = $null
                try {
                    $ComputerName = (Get-Content -Path $File.FullName |
                        Where-Object {$PSItem -like '@TargetNode*'}).Split('=')[-1].Trim("'")
                } catch {
                    $NoTargetNode += "$($File.Name);"
                } # End Try-Catch.

                If ($ComputerName) {
                    $Object = [pscustomobject]@{
                        ComputerName = $ComputerName
                        Guid = $File.BaseName
                    }
                    Write-Output -Verbose $Object
                } # End If.
            } # End ForEach.

            If ($NoTargetNode) {
                Write-Verbose -Message "---MOF Files without @TargetNode section---"
                $NoTargetNode = ($NoTargetNode.Trim(';')).Split(';')
                ForEach ($Node in $NoTargetNode) {
                    Write-Verbose -Message ($Node)
                } # End ForEach.
            } # End If.
        } Else {
            Write-Verbose -Message 'Cannot locate any MOF files.'
        }
    } # End Process.

    End {
        Write-Verbose -Message 'Function is done running.'
    } # End End.
} # End Function.

Below is an example of the output that will be displayed when we invoke the function against the directory that holds our <guid>.mof files for our DSC Pull Server.

ComputerName                                                       Guid
------------                                                       ----
serverX.mydomain.com                                               2766ffba-0c66-4358-8426-1a216c2b9d25
serverY.mydomain.com                                               7699bbcd-1a32-1429-9831-0f197d3a9b14
serverZ.mydomain.com                                               3224abda-2b41-4925-2948-4c317a1c0a54

 

Clear-Host, Without Clearing the Host – Part 2 (TMConsole Module)

Download the TMConsole Module here: https://gallery.technet.microsoft.com/TMConsole-Module-Clear-487eff0e

Back in early September, I wrote a post about “clearing” the host. I was proud of my simple, little function, that I then called clx, and so in early October, I linked the post on Twitter. To my surprise, a few well-known names in Windows PowerShell helped promote the blog post. I’m talking about PowerShell MVPs, authors, and community bloggers – the very people I look up to and learn from.

Just recently, I decided it would be wise to add comment-based help and some minor logic-based, error checking to the function, and then post it on the Microsoft TechNet Gallery. In addition, I wanted to combine it with another simple console function that I use, and upload them both as a module.

If you’ve read the link above then you already know about the first half of the module (the Clear-TMConsole function, aka clx). The second half of this module (the New-TMConsole function) simply opens a new PowerShell console and provides the user the option to keep, or close the current console. I wrote this function because there are many times when I want a fresh console that doesn’t have any left over variables. I was tired of typing Start-Process (or saps) powershell. I understand these are simple, but I’ve become quite dependent on them to speed up my work, and to mildly safeguard information I want to keep in my console(s).

Below are a few aliases I use to help speed up my use of the functions. Opening a new console, and closing the current one, is as simple as entering: nc y n and pressing enter. The y is to indicate to continue running the New-TMConsole function, and the n indicates to not keep the current console. Hopefully this module can be helpful to others, too.

Set-Alias -Name nc -Value New-TMConsole
Set-Alias -Name clx -Value Clear-TMConsole
Set-Alias -Name cc -Value Clear-TMConsole

Download the TMConsole Module here: https://gallery.technet.microsoft.com/TMConsole-Module-Clear-487eff0e