Category Archives: Script Sharing

Resolve TinyURL Using PowerShell

I received a sketchy email today. As I briefly scanned it, I noticed that the email included a TinyURL, and I thought: It would be nice if Outlook could resolve that URL, so I had a better idea where it resolved (without the need to use the URL). That took me out to find such a service. Not for Outlook, so much, but you know, a service that could complete this URL resolution. After I had tested that GetLinkInfo.com gave me what I wanted, I chose to use it’s URL inside a PowerShell function, and so here we are.

I’m not spending much time on this post, but the following function will open the website in your browser, so you can see the URL resolution information. If you include the OpenSite parameter, the function will even open a browser window to the resolved site itself. You might use that one with caution. At current, this function will require you leave PowerShell to see the function’s results, since it was designed to open a web browser window, or windows, depending on how you use it. Perhaps in time I’ll come back around and change that one day.

Function Get-TinyUrlLinkInfo {
<#
.SYNOPSIS
    This function relies on GetLinkInfo.com to determine the redirection of a TinyUrl URL without the need to visit the URL.

.DESCRIPTION
    This function relies on GetLinkInfo.com to determine the redirection of a TinyUrl URL without the need to visit the URL. If GetLinkInfo.com changes how they form their URLs, then this function may no work without some code edits.

.PARAMETER Url
    This parameter name is mandatory and requires a valid URL, such as http://tinyurl.com/ol499jx.com, be provided as the parameter value.

.EXAMPLE
    PS > Get-TinyUrlLinkInfo -Url 'http://tinyurl.com/ol499jx'.
    This example will open a web browser to GetLinkInfo.com and indicate that the TinyUrl resolves to the PowerShell Gallery.

.EXAMPLE
    PS > Get-TinyUrlLinkInfo -Url 'http://tinyurl.com/ol499jx' -OpenSite
    This example will open a web browser to GetLinkInfo.com and indicate that the TinyUrl resolves to the PowerShell Gallery. It will also open the resolved link in a browser. It's not recommend to include this parameter until after the redirected site is considered safe and trusted. Use this parameter with caution.

.NOTES
    Name: Get-TinyUrlLinkInfo
    Author: Tommy Maynard
    Comments: -
    LASTEDIT: 10/31/2016
#>
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true)]
        [string]$Url,

        [switch]$OpenSite
    )

    Begin {
    } # End Begin.

    Process {
        # Check link against GetLinkInfo.com.
        [void][Reflection.Assembly]::LoadWithPartialName("System.Web")
        $ConvertedUrl = [System.Web.HttpUtility]::UrlEncode($Url)
        Start-Process -FilePath "http://www.getlinkinfo.com/info?link=$ConvertedUrl&x=81&y=8"

        # Open resolved URL.
        If ($OpenSite) {
            Start-Process -FilePath $Url
        }
    } # End Process.

    End {
    } # End End.
} # End Function: Get-TinyUrlLinkInfo.

Compare AWSPowerShell Versions II

Series links: Part I, Part II, Part III (Coming)

I wrote a post a day or two ago that indicated how to compare your currently installed AWSPowerShell module with the offering on the PowerShell Gallery (see Part I above). Well, as I suspected, I’m back with an update.

Before we get there, however, I want to mention that Steve Roberts, at AWS, got in touch with me via Twitter about the post. I suspect we’ll be seeing some updates to Get-AWSPowerShellVersion. What’s interesting, is that I found the ListServiceVersionInfo parameter after my post. It seems that the cmdlet does create objects for some of the results it can return, just not everything. As always, I’m looking forward to Steve’s ideas and implementation.

Today, I’m going to share a quickly written advanced function. This will do the comparison for you and return the results in a custom object. Don’t be surprised if I make some more additions and/or changes to the function and post that as well. It really should have an option to download and install the new module if you want it, right?

Function Compare-AWSPowerShellVersion {
<#
.SYNOPSIS
    This advanced function compares the currently installed version of the AWSPowerShell module and the version on the PowerShell Gallery (http://powershellgallery.com).

.DESCRIPTION
    This advanced function compares the currently installed version of the AWSPowerShell module and the version on the PowerShell Gallery (http://powershellgallery.com).

.EXAMPLE
    PS > Compare-AWSPowerShellVersion
    This example compares the currently installed version of the AWSPowerShell module and the version on the PowerShell Gallery.

.NOTES 
    NAME: Compare-AWSPowerShellVersion
    AUTHOR: Tommy Maynard
    COMMENTS: --
    LASTEDIT: 10/19/2016
    VERSION 1.1:
        - Edited notes.
        - Changed Switch to -regex and made first option an OR for both side indicators.
    VERSION 1.2:
        - Modified results to use a custom object.
    VERSION 1.3:
        - Removed Switch statement: added version determination logic inside custom object creation.
        - Only returning highest version number from currently installed if more than one version. This is a leftover due to AWS Toolkit install vs. PowerShell Gallery install.
#>
    [CmdletBinding()]
    Param (
    )

    Begin {
        # Set continuation variable for Process block.
        $Continue = $true

        'PowerShellGet','AWSPowerShell' | ForEach-Object {
            Write-Verbose -Message "Determining if the $_ module is available."
            If (-Not(Get-Module -Name $_ -ListAvailable)) {
                Write-Warning -Message "Unable to locate the required $_ module."
                $Continue = $false
            }
        } # End Foreach.
    } # End Begin.

    Process {
        If ($Continue) {
            Write-Verbose -Message 'Collecting the current and newest AWSPowerShell module versions.'
            $CurrentAWSPSModule = ((Get-Module -Name AWSPowerShell -ListAvailable).Version |
                Sort-Object -Descending | Select-Object -First 1).ToString()
            $NewestAWSPSModule = (Find-Module -Name AWSPowerShell).Version.ToString()

            Write-Verbose -Message 'Comparing the AWSPowerShell version and creating custom results object.'
            [PSCustomObject]@{
                Match = If ($CurrentAWSPSModule -eq $NewestAWSPSModule) {$true} Else {$false}
                Current = $CurrentAWSPSModule
                Newest = $NewestAWSPSModule
            }
        } # End If.
    } # End Process.

    End {
    } # End End.
} # End Function: Compare-AWSPowerShellVersion.

Update: After updating to the newest version, I was returning a System.Object[] error for my current version, due to having multiple versions and using the ToString method. I’ve modified the code above to only select the first (and highest) module version from those on my system. I think it’s important to point out that I’ve always downloaded and installed using the .msi until this version, and that when I used Install-Module, I included the Scope parameter name with the CurrentUser parameter value. I suspect this is where the problem is originating. Well, this and the fact I haven’t uninstalled the .msi version from Programs and Features.

I shouldn’t have to wait too long to be on an old build to test some more! It seems as though AWS does at least one, daily build. Here’s the full command as I ran it to install the newest version: Find-Module -Name AWSPowerShell | Install-Module -Scope CurrentUser.

Here’s the usage and results since updating to 3.3.11.0.

PS > Compare-AWSPowerShellVersion

Match Current  Newest  
----- -------  ------  
 True 3.3.11.0 3.3.11.0

Update: As suspected, there’s a new build this evening, and it appears the comparison function is still working, as it’s correctly indicated there isn’t a match between what’s installed and what’s available to install.

PS > Compare-AWSPowerShellVersion

Match Current  Newest
----- -------  ------
False 3.3.11.0 3.3.12.0

I updated to the newest version of the module and ran it again. It’s looking good.

PS > Compare-AWSPowerShellVersion

Match Current  Newest
----- -------  ------
 True 3.3.12.0 3.3.12.0

AWS EC2 Instance Type Count

There’s a project that I’m on that allows me to work in AWS. It’s pretty important stuff at work, and since there’s an AWS PowerShell module, I couldn’t be more interested. As of today, I’m going to start to include worthy AWS PowerShell work, right here. As I become more involved, so will my blog.

I’ve said it a bunch of times now, but this site isn’t just about helping others — although that’s a big part — but it’s about giving myself a place to store the things, I think I’ll want one day. With over 2,000 cmdlets in version 3.3.0.0 of the AWSPowerShell module, I’m going to need a place to store this, and any help I can get in retaining this information, I’ll take. Writing helps with that; you know, the whole writing to remember concept.

So, here’s the error I was up against today:

“Your quota allows for 0 more running instance(s). You requested at least 1 – You can request an increase of the instance limit here: http://aws.amazon.com/contact-us/ec2-request/.”

As relayed by our upcoming, but practically already there, AWS expert, “Each instance size (t2.small, c3.large, etc) has a separate quota in each region in AWS.” The decision to do this was preventative in that we wouldn’t accidentally spin up too many instances and cost ourselves dearly. I get that.

I’m a curious type, so I couldn’t help but wonder, how many of each instance type do we have in the current AWS account? I switched over to my PowerShell console and started writing. This is straightforward and basic PowerShell, but it still seemed like something worth keeping around, and sharing, as well.

$Instances = Get-EC2Instance

Foreach ($Instance in $Instances) {
    [array]$Types += $Instance.Instances.InstanceType
}

$Types | Group-Object -NoElement

The example works this way: Line 1: Capture all the EC2 instances, Line 3: Begin to iterate over the returned collection. With each pass, in line 8, append the current instance’s instance type to the $Types variable — an array. Finally, in line 7, we group the $Types variable in that it’ll automatically provide the Count property for each Instance Type.

I’ve included a slight modification in the example below. In this instance, I didn’t cast the $Types variable as an array, and instead created the variable as an empty array prior to the Foreach. Same end goal, however, I wanted to highlight how others might be inclined to write something similar.

$Instances = Get-EC2Instance

$Types = @()
Foreach ($Instance in $Instances) {
    $Types += $Instance.Instances.InstanceType
}

$Types | Group-Object -NoElement

If you run this repeatedly, you’ll quickly realize that it’ll continue to add to the $Types variable, thus making the results incorrect as soon as the second run. You can add this as the last line: Remove-Variable -Name Types, or better, make it a function.

Function Get-EC2InstanceTypeCount {
<# #>
    [CmdletBinding()]
    Param (
    )

    Begin {
        $Instances = Get-EC2Instance
    } # End Begin.

    Process {
        Foreach ($Instance in $Instances) {
            [array]$Types += $Instance.Instances.InstanceType
        }

        $Types | Group-Object -NoElement
    } # End Process.

    End {
    } # End End.
}

Remember, you’re going to need to have already used Set-AWSCredentials and stored a set of credentials to a persistent store. This function is dependent, just like any AWS cmdlet, on there being a set of stored credentials. I did not write the function to accept an AccessKey and SecretKey parameter, as this isn’t a recommended scenario in much of the documentation I’ve read from AWS. Here’s the function — with comment-based help — if might be useful for your team.

Function Get-EC2InstanceTypeCount {
<# .SYNOPSIS This advanced function will return the AWS Instance Types used in an AWS account. .DESCRIPTION This advanced function will return the AWS Instance Types used in an AWS account. It will return a name property, such as t2.medium, m4.large, etc., and a Count property. The results are sorted on the count, as they are produced using the Sort-Object cmdlet. .EXAMPLE PS > Get-EC2InstanceTypeCount
    This example returns the EC2 Instance Types and how many of each are being used.

    Count Name
    ----- ----
    32    t2.medium
    18    t2.micro
     6    c3.large
     6    m4.large
     7    t2.small
     2    r3.large
     4    r3.xlarge
     5    g2.2xlarge
     5    t2.large
     1    t2.nano

.EXAMPLE
    PS > Get-EC2InstanceTypeCount | Sort-Object -Property Name
    This example returns the EC2 Instance Types and how many of each are being used, sorted by Name.

.EXAMPLE
    PS > Get-EC2InstanceTypeCount | Where-Object -Property Name -like *large*
    This example returns the EC2 Instance Types that include the term "large" in the Name property.

    Count Name
    ----- ----
        6 c3.large                 
        6 m4.large                 
        2 r3.large                 
        4 r3.xlarge                
        5 g2.2xlarge               
        5 t2.large

.NOTES
    NAME: Get-EC2InstanceTypeCount
    AUTHOR: Tommy Maynard
    COMMENTS: --
    LASTEDIT: 09/27/2016
    VERSION 1.0
#>
    [CmdletBinding()]
    Param (
    )

    Begin {
        $Instances = Get-EC2Instance
    } # End Begin.

    Process {
        Foreach ($Instance in $Instances) {
            [array]$Types += $Instance.Instances.InstanceType
        }

        $Types | Group-Object -NoElement
    } # End Process.

    End {
    } # End End.
}

Creating Multiple Credential Objects Part II

Download the updated function here: https://gallery.technet.microsoft.com/Specify-and-Create-d80ad39e

Back in January 2015, I wrote a post and uploaded an advanced function to the TechNet Gallery that allows a user to create multiple credential objects. The multiple part is up to the user, such as they might enter a command as in the below example. Due to the -Set 2 parameter and parameter value, they would get prompted twice, to enter a username and password combination. When the function was completed, they would have two credential objects, with the first stored in $CredSet1 and the second stored in $CredSet2.

New-TMMultiCred -Set 2

I had always wanted to make a couple changes to the advanced function, and so I have. Now the function includes a -NoGui parameter, that will not require the username and password be entered into the Get-Credential GUI, and can instead be entered directly into the console. I should mention that if this was run in the ISE, the password would actually invoke a small GUI for the password, as is standard when using Read-Host’s -AsSecureString parameter in that host.

The other addition I wanted to add is that the advanced function produces objects, instead of using the Write-Output cmdlet to display information. Now, instead of the function writing this:

SUCCESS: Credential Set 1 stored in $CredSet1
SUCCESS: Credential Set 2 stored in $CredSet2

the function writes this:

CredSet             Variable            UserName                                 Password
-------             --------            --------                                 --------
1                   $Credset1           mydomain\admin       System.Security.SecureString
2                   $Credset2           user1                System.Security.SecureString

Neat, right? So download it and try it out. I think it was a decent addition to an already helpful advanced function.

Download the updated function here: https://gallery.technet.microsoft.com/Specify-and-Create-d80ad39e

Write-Output Gets Foreground and Background Colors and More

Every once in a while a forum post comes along that really captures my interest. Take this one for instance.

In this post, the creator wanted to build out a start up menu in the ConsoleHost that has a solid white line, another white line below that, with the words “PowerShell Project 1” in it, and a final white line beneath that. That might be difficult to imagine, so here’s a visual representation, and then what his/hers looked like. I had recommended to use a here-string to fix the problem, but unfortunately we didn’t have the same results.

write-output-gets-foreground-and-background-colors-and-more01
Note: While you can’t see them, in both this and the below image, there are spaces on the lines above, below, and before and after “PowerShell Project 1.” This is how we can ensure the white rectangle around the text.

write-output-gets-foreground-and-background-colors-and-more02

I can’t say for certain why theirs rendered differently, but I suspect version, or PSReadline, or possibly console font/size. That wasn’t important; what was, was trying to figure out a way around this problematic inconsistency. We have the option to change the background color of the area where we type, and so I wondered if I could temporarily do that inside the ConsoleHost. It turns out I could. With that new piece of knowledge, I set out to write a wrapper function around Write-Output. In my version — Write-TMOutput — it includes -ForegroundColor and -BackgroundColor parameters with an option to horizontally and vertically pad the text (object).

First, here’s a few images to show my new function in action. This first image shows the commands included in the TMOutput module.

write-output-gets-foreground-and-background-colors-and-more03

The next example image shows some basic, Write-Output type usage. One where we pipe to the function, and one where we don’t. Fairly standard.

write-output-gets-foreground-and-background-colors-and-more04

Now, the next example is where this starts to get fun: the incorporation of the -ForegroundColor and -BackgroundColor parameters. Prior to running these commands, and those in the last two examples, I removed the PSReadLine module so the colors would be easier to spot.

write-output-gets-foreground-and-background-colors-and-more05

Next is a demo of two additional parameters: -HorizontalPad and -VerticalPad. These will allow you to add spaces before and after the value supplied to the -InputObject parameter (the text), and add lines above and below the text. This is the part that gets back to the request in the Microsoft Technet forum post. I should mention that my solution in the forum post would be different now that I’ve spend some time writing this function — there are better ways to do things that I hadn’t considered at the time I replied to the post.

write-output-gets-foreground-and-background-colors-and-more06

The next example shows some usage with the Get-ADUser cmdlet. Keep in mind that if you combine this function with other cmdlets, that the -HorizontalPad and -VerticalPad parameters cannot be used. In my mind, they’re just a bonus to the option of using colors with Write-Output. I should mention it now, but this function was intentionally written to only work in the ConsoleHost. Perhaps I’ll add the option of using it in the ISE in the future.

write-output-gets-foreground-and-background-colors-and-more07

I’ve written an additional function that will allow you to quickly see the available colors from which you can choose. It’s called Show-TMOutputColor, and produces the results below. This might come in handy as you’re using the modified Write-Output.

write-output-gets-foreground-and-background-colors-and-more08

Now, on to the module. It’s the first time I’ve done it, but I’ve uploaded this to the PowerShell Gallery. I believe publishing to the PowerShell Gallery was one of my PowerShell New Year’s Resolutions for 2016, so there’s one down! You can use this direct link or, one better, download the module from your console, using this command:

Install-Module -Name TMOutput

Thanks for reading this post. Hopefully these function will be a useful way to get around the need for Write-Host’s foreground and background color options while working in the console. That, and maybe the TechNet thread creator can do whatever they wanted, too.

Multi-Level Menu System with a Back Option

It’s been said several times now, but while this site was designed to help people learn some Windows PowerShell, it’s also about posting things I’m going to want to find one day. Take for instance this menu system I started for someone on TechNet: https://social.technet.microsoft.com/Forums/en-US/30663446-4091-4a1c-9de0-407046ccc39f/powershell-script-with-submenus-how-to-go-back?forum=winserverpowershell.

It allows the user the ability to enter into menus and submenus with the option of backing out of them. You know, choose a number from the menu, or hit B to go back to the previous menu. Hopefully it’s helpful for the TechNet thread creator, and maybe it’s helpful for others someday, too. I’ve included both the code and an image of the code in action. Until next time.

Do {
@'

----------Software/Driver Installation----------
1. Toshiba 1
2. Acer 1
------------------------------------------------

'@

    $MainMenu = Read-Host -Prompt 'Enter 1 - 2 or Q to quit'
    Switch ($MainMenu) {
        1 {
            Do {
@'

---------Software/Driver Installation----------
1. Software
2. Drivers
------------------------------------------------

'@
                $1MainMenu = Read-Host -Prompt 'Enter 1 - 2 or B for Back'
                Switch ($1MainMenu) {
                    '1' {
                            Do {
@'

--------------Software Installation-------------
1. Package 1
2. Package 2
3. Package 3
------------------------------------------------

'@
                                $1InnerMenu = Read-Host -Prompt 'Enter 1 - 3 or B for Back'
                                Switch ($1InnerMenu) {
                                 '1' {Write-Output -InputObject '--> You chose to install package 1'; break}
                                 '2' {Write-Output -InputObject '--> You chose to install package 2'; break}
                                 '3' {Write-Output -InputObject '--> You chose to install package 3'}
                                }
                            } Until ($1InnerMenu -eq 'B')
                        }
                }
            } Until ($1MainMenu -eq 'B')
        }
    } # End Switch.
} Until (
    $MainMenu -eq 'Q'
)

multi-level-nested-menu-system-with-a-back-option01

View Current PowerShell.org Q&A Forum Topics

Note: Update added at the bottom on this post on August, 9, 2016. Please read.

Sometimes you don’t always have the time to finish something you’ve started. For me, it was this function. I pounded this out in a quick few minutes, and while I don’t see myself investing in it any further, I didn’t want to forget the function, and thought I would hang on to it somewhere. Well, that’s why it’s here, especially as someone may find it useful, or helpful.

The function, which I called Get-PowerShell.orgForumTopic, runs out to PowerShell.org and grabs the current topics (page one) from the PowerShell Q&A forum (http://powershell.org/wp/forums/forum/windows-powershell-qa). It only returns the Thread name and the URL, because, well, that’s what seemed useful and relevant at the time I wrote it (which was many months ago).

Function Get-PowerShell.orgForumTopic {
    [CmdletBinding()]
    Param ()

    Begin {
    } # End Begin.

    Process {
        (Invoke-WebRequest -Uri 'http://powershell.org/wp/forums/forum/windows-powershell-qa/' |
            Select-Object -ExpandProperty Links |
            Where-Object {$_.outerHTML -like '*http://powershell.org/wp/forums/topic*'} |
            Select-Object @{N='Thread';E={$_.innerHTML}},@{N='Url';E={$_.href}} |
            Select-Object -First 30)[(0..30 |
                ForEach-Object {
                    If (-not($_ % 2)) {
                        $_
                    }
                 }
            )]
    } # End Process.

    End {
    } # End End.
} # End Function: Get-PowerShell.orgForumTopic

Here’s what the results looked like in the ConsoleHost, near in time to when this post was published.

current-powershell.org-qa-forum-topics-01

While I never added any more to this function, I had some ideas: add the thread status, add the “started by user,” add the user that made the last post, add the number of posts per topic, and allow it to run against other PowerShell.org forum topics. It might’ve also been helpful to include additional pages, if requested by the user of the function, such as adding a -Pages parameter (-Pages 4).

Anyway, here it is. Beside being helpful to see the top PowerShell.org Q&A forum posts at a specific point in time, it’s an interesting example of reading from a webpage, of which I had minimal experience. So yeah, I probably learned something by doing this exercise.

If you want to do the same, then start by running the first command, Invoke-WebRequest -Uri ‘http://powershell.org/wp/forums/forum/windows-powershell-qa/’. Then add the pipe and first Select-Object command and run that. Then add the Where-Object command, and so on. This will allow you to see how I finally got to only returning the “Threads” and “Urls.” Take care.

Update: Since a site redesign at PowerShell.org, this function, no longer functions. I’m not sure that I’ll bother to update it — I don’t get the feeling that it was ever used by anyone — but I’ll keep this post up for anything else it may offer, that may be helpful in learning PowerShell.

Find the Account that Starts a Service

There was an email I wrote to a team member last week. The point behind it was to help the team member determine what service(s) might be running under a certain account. The Get-Service cmdlet has never provided this functionality, so I mentioned that they use the Win32_Service WMI class (with Windows PowerShell, of course).

This suggestion led to me write and send them a list of command examples to include local vs. remote computers, pulling computer’s from a text file and Csv file, and using Active Directory. It seemed like a fairly solid list, and so I thought it should be posted here. I won’t bother including any output, because it’s fairly easy to figure out what’s going to be returned — the name of the service, the account used to start/run the service, and the remote computer name, in some of the commands that run against remote computers.

This first example is how to run the command against the local computer. This will return all the services on the local computer to include the account that is used to start the listed service.

PS> Get-WmiObject -Class Win32_Service | Select-Object Name,StartName

This example shows how to run the same command above, against a single remote computer. The only difference is the addition of the -ComputerName parameter and the Server01 parameter value.

PS> Get-WmiObject -ComputerName Server01 -Class Win32_Service | Select-Object Name,StartName

In this example, we run the same command above, except we do run it against two computers. In order to know which computer returned which result, we include the PSComputerName property.

PS> Get-WmiObject -ComputerName Server01,Server02 -Class Win32_Service | Select-Object PSComputerName,Name,StartName

Here’s one way to use a text file. The Get-Content cmdlet executes first, because it’s in parenthesis. The results of this command are then provided to the -ComputerName parameter.

PS> Get-WmiObject -ComputerName (Get-Content -Path C:\computers.txt) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

This example does the same thing as the command above; however, it does so by piping the results of the Get-Content cmdlet to the ForEach-Object cmdlet. Within each iteration of the foreach loop, it runs the Get-WmiObject command. In testing, there wasn’t much different in the time it took to complete this example when compared to the last one.

PS> Get-Content -Path C:\computers.txt | ForEach-Object {Get-WmiObject -ComputerName $_ -Class Win32_Service | Select-Object PSComputerName,Name,StartName}

This next example utilizes a Csv File. In a Csv file, we have (column) headers; therefore, we need to indicate to PowerShell the column we’d like to use. Let’s assume we have a Csv with three columns of data with the headers Location, NameOfComputer, and Year. We’ll need to make sure we’re instructing our command to only use the data in the NameOfComputer column.

PS> Get-WmiObject -ComputerName ((Import-Csv -Path C:\computers.csv).NameOfComputer) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

Here’s the same example as above, but this one uses a pipeline and ForEach-Object cmdlet, much like we did with the text file. While there’s little difference in the time it takes to complete, I would recommend that we don’t use a pipe when it’s not necessary, or when it’s doesn’t give us an advantage.

PS> Import-Csv -Path C:\computers.csv | ForEach-Object {Get-WmiObject -ComputerName $_.NameOfComputer -Class Win32_Service | Select-Object PSComputerName,Name,StartName}

Let’s incorporate Active Directory (AD), since it’s another place where we can get computers. This first AD example returns all the computers from a specific Organization Unit (OU) and runs them through the Get-WmiObject cmdlet. As in previous examples, this command will return all the values before it provides them as the value to the -ComputerName parameter.

PS> Get-WmiObject -ComputerName ((Get-ADComputer -Filter * -SearchBase 'OU=Engineering,OU=Workstations,DC=mydomain,DC=com').Name) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

In my coworker’s situation, one of the computers in the OU was down, so we needed a way to filter around that problem. Here’s two ways to do that; however, the second option is the better of the two. If you’ve never heard it before, hear it now: filter as close to the left of your commands as possible.

PS> Get-WmiObject -ComputerName ((Get-ADComputer -Filter * -SearchBase 'OU=Engineering,OU=Workstations,DC=mydomain,DC=com' | Where-Object {$_.Name -ne 'Server22'}).Name) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

PS> Get-WmiObject -ComputerName ((Get-ADComputer -Filter {Name -ne 'Server22'} -SearchBase 'OU=Engineering,OU=Workstations,DC=mydomain,DC=com').Name) -Class Win32_Service | Select-Object PSComputerName,Name,StartName

 

Two Old HTAs: LoggedOnUser and Remote Desktop Assistant

There was a recent Windows PowerShell forum post that discussed HTAs. Due to that, I thought I would discuss a couple old HTAs I wrote and include their download links. Many people may not know it, but I was once a VBS aficionado. I had a task to set some registry settings back in 2005, or 6, and ended up staring some VBS examples right in the face. I was instantly hooked. I starting writing all kinds of things, and today I’m going to share two of my old HTAs — a couple favorites.

HTAs are HTML Applications. They allowed script writers, such as myself, the ability to create GUIs for their scripts. I suddenly had a way to hand over scripts in an easy to use fashion for users, and other IT personnel.

Funny story, but when Windows PowerShell — a.k.a Monad — was first introduced, I was excited at first, but then I was pissed (upset, not drunk). I had put so much time and effort into VBS, that moving to something else was upsetting. I’ve gotten over that now; I would need an extremely large pay day, to write and/or troubleshoot any VBS.

The first of the two HTAs I’ll share is called LoggedOnUser (1.6). Its original purpose was to determine who was logged on to a specific computer. Here’s what the HTA looks like when it’s first opened.

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-01

While you may not know what to do right away, there’s a tooltip that displays when you hover over the textbox, that reads, “Enter computer name, IP address, cmd or exit.” If someone entered “cmd” and pressed Enter, it would open the command prompt. I guess that seemed important at the time. If you entered “exit” and pressed Enter, then the HTA would close. Hopefully no one had a computer named cmd or exit! I wrote this for myself, so I assume I wasn’t worried about that possibility. If a computer name or IP is entered, it would attempt to connect over the network (using VBS and WMI), and populate three fields: Computer (as in the computer’s name), the currently logged on user as domain\user, and the full domain. Here’s an example:

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-02

After some time had passed, I decided to do more with this daily used HTA. I added a drop down menu that would perform different tasks, such as open C$, open Computer Management, and start a Remote Desktop Connection to the listed computer.

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-03

It was a fun project and the HTA was actively used from 2007 to probably 2013, by myself and at least one other IT shop. In my 1.5 version (one previous to this 1.6 version), there was a drop down option that opened up an inventory system to the listed computer’s inventory webpage. The inventory system had been built using a .vbs start up script deployed via Group Policy. It would collect computer information (including the logged on user), and write it to an Access database back end (yes, Access). Classic ASP was used for the web front (yes, classic ASP). Good times. I can say this: learning VBS made learning classic ASP extremely easy.

You can download this HTA here: LoggedOnUserv1.6 (246 downloads)

The second HTA I’ll share was an amazing test of my patience. I’d like to thank Tool’s 10,000 Days for getting me through it.

I called this one Remote Desktop Assistant and its purpose was to store computer descriptions and matching IP addresses, or computer names. Choosing a computer from the list would allow a user to start a Remote Desktop Connection. This first screen shot shows the HTA when there are no stored computers. Let’s add one.

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-04

This image below, is what it looks like when it’s storing a computer for us. It stores this information in a computers.txt file in the same location as the HTA. Move the HTA and not the text file, and no computers will be listed, and the HTA will create a new computers.txt file. Yes, it’s creates this file if it can’t find one. With a single computer entered, such as we have below, the text file would contain one line that reads: Server01;10.10.10.35. It’s two strings with a semi-colon delimiter.

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-05

The server (Server01), in the image above was added by entering a description in the “Computer Description” textbox, and a corresponding computer name or IP address in the “IP Address or Computer” text box. When a user clicks on the computer in the left, it populates the the two textboxes and changes the “Choose Computer” button to a “Launch Remote Desktop” button. Do you see all this fanciness!?

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-06

If you press “Launch Remote Desktop” then it will do just that. The three bottom buttons: About…, Notes, and Feedback, offer some additional information about the HTA. It’s nothing monumental, but I was pleased with my ability to resize the form and replace the text in the lower area. The Hide button closes the lower, informational panel. Take a look below to see what I mean.

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-07

script-sharing-two-old-htas-loggedonuser-and-remote-desktop-assistant-2015-08

Well, that was a fun trip down memory lane. Who knows, these might actually be helpful for someone. One of the things I did, which I may share in a future post, was rewrite the LoggedOnUser VBS/HTA using PowerShell. There’s no GUI, but it does everything that HTA does, way faster, and with much less code. As you check these out, please keep in mind I wrote these around 2005 – 2007, or 8. Cheers!

You can download this HTA here: RDAssistantv2.1 (299 downloads)

Update: Use PowerShell and Active Directory to create your computers.txt file: http://tommymaynard.com/quick-learn-prep-computers-txt-file-for-hta/.

Script Sharing – Remote Desktop Prompts for Multiple Computers

Yes, I still RDP* (occasionally). I am using it less and less, but there are still times when getting inside a system’s GUI is seems necessary. I’m into this Windows PowerShell thing, if you haven’t noticed, so you can rest assured that every time I RDP, I also do my best to figure out how to get the same information using PowerShell. In fact, this often leads to new functions and tools. I just remembered this, but in the earlier days of learning PowerShell I would try and replicate everything I did in the GUI, in PowerShell. If you’re wondering how to learn PowerShell, well then, there you go.

* RDP stands for Remote Desktop Protocol. While you can’t Remote Desktop Protocol into a server, the acronym is often used as a verb: “Hey, RDP over to bigserver1 and let me know if you see the same problem.”

Anyway, let me share a function that I occasionally use. Take a look at the code, and then I’ll briefly discuss it below.

Set-Alias -Name rdp -Value Connect-TMRDP
Set-Alias -Name rdc -Value Connect-TMRDP
Function Connect-TMRDP
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory=$true,Position=1,ValueFromPipeline=$true)]
        [Alias('cn')]
        [string[]]$ComputerName,

        [Parameter(Position=2)]
        [string]$DomainName
    )
    Begin {
    } #End Begin

    Process {
        If ($DomainName) {
            Foreach ($Computer in $ComputerName) {
                If ($DomainName -match '^\.') {
                    mstsc.exe /v "$Computer$DomainName"
                } Else {
                    mstsc.exe /v "$Computer.$DomainName"
                }
            }
        } Else {
            Foreach ($Computer in $ComputerName) {
                mstsc.exe /v $Computer
            }
        }
    }
}

This function allows me the ability to remote desktop into several systems at a time. While it won’t enter user names and passwords for me, it’ll present me the Windows Security logon dialog for each computer name supplied as a value to the -ComputerName parameter. That’s why I wrote it. If you’ve ever used mstsc.exe /v, and got tired of typing that over and over for each machine, now you can save time by typing something like rdp dc01,dc02,web01 and it’ll open a logon dialog for each. The rdp (and rdc) alias is created right before the function — a function I’ve named Connect-TMRDP.

Maybe this helps, may it doesn’t, but it sure speeds up the time it takes me to RDP to several servers at once. I know, I know — the simple fact that I need to RDP to several computers at once makes an even better case for PowerShell. Again, continuing to use RDP on occasion has led to some nice PowerShell tools, and I expect that this will continue.

I did want to mention one final thing about this function. It’s set up to handle the domain name being included in several different ways, if it’s included at all. Each of the examples below will work.

PS> rdp -ComputerName bigserver01
PS> rdp -ComputerName bigserver01.mydomain.com
PS> rdp -ComputerName bigserver01 -DomainName .mydomain.com
PS> rdp -ComputerName bigserver01 -DomainName mydomain.com