PowerShell Community and the Windows System Administration Tool

PowerShell Community and the Windows System Administration Tool

  • Comments 9
  • Likes

Summary: See how guest blogger, Rich Prescott, leveraged the Windows PowerShell community as he built his popular ArPosh Client System Administration tool.

Microsoft Scripting Guy, Ed Wilson, is here. We are really starting the new year off correctly. We have another very strong guest blogger today. Rich Prescott, is currently working as an infrastructure architect and Windows engineer at a large media company, with his main areas of focus on automation and Active Directory. The automation responsibilities allow him to work with a wide-range of technologies including virtualization, storage, and monitoring. He started learning Windows PowerShell in 2009 while he was working as a desktop engineer, and he is the lead scripting guy for his organization. He is also a moderator on the Official Scripting Guys Forum, and was recently rewarded as a Microsoft Community Contributor.

Blog: Engineering Efficiency: Scripts, Tools, and Software New in the IT World
Twitter: @Rich_Prescott

Take it away Rich…

When I first joined the IT world, I was working at the Help desk for a large company. Being new to IT and troubleshooting, I always wondered why there was no single tool for troubleshooting remote computers. I would receive a call about a computer being slow, and I would have to go through four tools to try to diagnose the issue. I began searching for an easy solution, and that is when a coworker introduced me to Windows PowerShell. After reading blog posts from Hey, Scripting Guy! and the Scripting Wife, I was writing basic scripts to gather the information that I needed for troubleshooting. This is an example of a basic script I wrote back then.

$PC = “PC01”
Get-WmiObject Win32_OperatingSystem –ComputerName $PC
Get-WmiObject Win32_StartupCommand –ComputerName $PC
Get-WmiObject Win32_Process –ComputerName $PC

After the underlying issue was targeted, I needed a way to remediate the issue, such as removing a hung process. This was possible remotely by querying the Win32_Process class on a remote computer, filtering for the process that was causing an issue, and invoking the Terminate method. Here is an example of using the Terminate method to stop a process on a remote computer.

(Get-WmiObject Win32_Process –ComputerName $PC |
Where-Object {$_.Name –eq “HungProcessName”}).Terminate()

At last, I was able to efficiently troubleshoot issues remotely and even resolve some of them without ever having to leave my desk. As I became more familiar with Windows PowerShell, I thought, “Wouldn’t it be cool if I could share the efficacy of Windows PowerShell with others, even if they don’t know any scripting?” After a few months of researching, scripting, and testing GUI creation with Windows PowerShell, the Arposh Client System Administration tool (ACSA) was released.

As with any technology that you learn rapidly, when you look back on what you were doing a year ago, you think to yourself, “What was I thinking when I wrote that? I could write that in half the code and make it twice as fast.” So I set out to find resources to help me rebuild the script from scratch, and I used this opportunity to remove some of the prerequisites, make it compatible with servers, and improve the overall user experience.

PowerShellGroup - #PowerShell chat room

One of the advantages of using Internet Relay Chat (IRC) is that there are chat rooms for almost any topic you can think of and when you are learning a new technology, having a live discussion can be really helpful. While revamping the ACSA tool, one of the user-experience features that I wanted to add was to give the user the ability to decide which feature sets to use through a configuration file.

I joined the #PowerShell chat room on PowerShellGroup.org, and I asked about an easy way to give users configuration options. Jaykul, a Windows PowerShell MVP, responded with a way to read settings from an XML file. By using the following XML code and three lines of Windows PowerShell, a user is able to set a default domain to connect to when using the GUI. To have the GUI use the current domain of the logged on user, simply update the Enabled option of the default domain to “False” in the XML configuration file.

<?xml version="1.0" standalone="no"?>
 <Domain Default="LDAP://DC=RU,DC=lab" Enabled="True"/>

In the first line of code, we use Get-Content on the XML configuration file and specify that it is XML code by using [XML]. We then check to see if the default domain option is enabled, and if so, we set the Domain variable to what is specified in the XML. If the option is disabled, the GUI sets the Domain variable to the currently logged on domain.

 [XML]$XML = Get-Content “AWSA.Options.xml”
 if($XML.Domain.Enabled –eq $True){$Domain = $XML.Domain.Default}
 else{$Domain = ([DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name}

TechNet Wiki

My next task was to make the GUI easy to use, and this meant removing any unnecessary pre-requisites included in the original release. My first stop was the TechNet Wiki to find a way to query Active Directory for computers without the need to import a Windows PowerShell module that was not freely available on all systems. After a quick search, I landed on a wiki contribution from Richard Mueller, another MVP, for ADSI searches, which showed me the syntax necessary to build my custom function.

 function Get-RPADComputer{
  if($ComputerName -match "."){$ComputerName = $ComputerName.Split('.')[0]}
  $Properties = $XML.Options.Search.Property

This function checks the $ComputerName parameter that is specified in the textbox of the GUI, and if a fully qualified domain name (FQDN) is specified, it translates it to a short name. It then creates a query that will search for any computers that match the new $ComputerName variable. The third line reads a list of properties that the user specifies in the XML settings file and adds them to the list of properties to load. The final line of the function executes the query and returns the results.

TechNet forums

After adding in the ability to set a default domain when launching the GUI, the user needed a way to change domains without having to edit the XML file and relaunch the script. I was not familiar enough with the ADSI scripting techniques that I found on the TechNet Wiki, and I decided to head over to the Official Scripting Guys Forum on the TechNet Forums and ask for some assistance.

I posted my question with some examples of the input and output that I wanted, and within four hours, I received multiple responses from the community. And what do you know—Richard Mueller again came to the rescue with a way to prompt for a domain and then convert the response into the LDAP path for that domain. By slightly tweaking his code, I was able to add the ability to search alternate domains to my custom Active Directory computer search function.

TechNet ScriptCenter

There are many new features in this update of the GUI, but I want to highlight one that many administrators will find very useful and sigh at the mention of: local administrator rights. Every system administrator has received a request along the lines of, “I need to install XYZ software right now!” or “I am using VPN from home, and I need to add a local printer.” One way to get around this (not always the best way) is to grant the user temporary local administrator rights. To find out how to do this the Windows PowerShell way, I headed over to the TechNet ScriptCenter Repository to search for some examples.

Using the Categories listing in the left pane, I drilled down into Local Account Management, and I immediately saw what I was looking for: Local User Management Module. I clicked through to the details page and looked through the included functions. I was in luck! By using ADSI code in the Set-LocalGroup function, I morphed it into my own function as shown here.

 Function Add-LocalAdmin {
  [System.Reflection.Assembly]::LoadWithPartialName('Microsoft.VisualBasic') | Out-Null
  $Input = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a username to add (Domain\Username)", "Add Local Admin", "")
  $Group = [ADSI]("WinNT://" + $ComputerName + "/Administrators, group")
  $Group.Add("WinNT://" +$Input.Replace(‘\’,’/’)

The first line of the function loads the Visual Basic assembly; this allows me to create a custom input box, which prompts the user for the username to add to the local administrators group and then stores it in the $Input variable. The function then binds to the Administrators group on the remote computer using ADSI. Now we call the Add method of ADSI to add the desired user account to the administrators group. But there is a snag, the username is specified as Domain\Username, whereas ADSI requires a forward slash. To get around this, we use the Replace method to turn the back slash into a forward slash.

Arposh Windows System Administration tool

Whether you are new to Windows PowerShell and only need a simple script to get you going or you are a Windows PowerShell guru and need a nudge in the right direction, there are numerous resources for everyone. Thanks to all of these resources, I was able to take a simple GUI that I created for myself to make tasks easier and improve it enough to where the Windows PowerShell community would also find it useful. Without further ado, here is the Arposh Windows System Administration tool.

Image of Arposh


I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • <p>Well done Rich :)</p>

  • <p>Thanks for sharing, Rich! It&#39;s amazing how quickly you&#39;ve learned all this and picked it up!</p>

  • <p>Is there a way to run this on the local PC that is not part of a domain? &nbsp;Very Nice App! &nbsp;Well done!</p>

  • <p>Amasing, great, very nice app.. I used it a few days a go.. juste writing for thanks .. </p>

  • <p>Hi Rich,</p> <p>thanks for sharing your learning experience and your resulting ACSA script with us!</p> <p>It&#39;s great to see what you manged to compose together to make it work!</p> <p>Well done!!</p> <p>Klaus (Schulte)</p>

  • <p>@Tim: &nbsp;Yes, this can be run on a local PC that is not part of a domain. &nbsp;However, as it was designed to work in a domain environment, you may see error messages when attempting certain functions.</p>

  • <p>It is always wonderful to see people contribute.</p> <p>But please, control your gushing accolades . It was built with Sapien&#39;s PrimalForms 2011.</p>

  • <p>Dear Rich,</p> <p>thanks for sharing this nice tool!! </p> <p>I&#39;m a new powershell user and just start to read about powershell some week ago. During this I find this great tool which I start to extend a bit. </p> <p>I have only one big problem, because I would like to change the color within the services &nbsp;for the column State that the running once appear in green and the stopped once in red, but I cant figure out in your script how to change specific columns.</p> <p>I can only change the back- and foreground color for the complete text field but not for a specific column.</p> <p>I try quite a few changes with the powershell command:</p> <p>get-service | sort-object name | foreach-object{ if ($_.state -eq &quot;stopped&quot;) {write-host -f green $_.name $_.state}` else{ write-host -f red $_.name $_.state}}</p> <p>inside the following code:</p> <p> &nbsp; &nbsp;$btnServices_Click={</p> <p> Get-ComputerName</p> <p> Initialize-Listview</p> <p> $SBPStatus.Text = &quot;Retrieving Services...&quot;</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;Update-ContextMenu (Get-Variable cmsSvc*)</p> <p> $XML.Options.Services.Property | %{Add-Column $_}</p> <p> Resize-Columns</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;$Col0 = $lvMain.Columns[0].Text # deactivating this code empty the Service name field, change to Process and go back to service the process ID appears in the name field from services</p> <p> $Info = Get-WmiObject Win32_Service -ComputerName $ComputerName -ErrorVariable SysError | Sort Name</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;Start-Sleep -m 250</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; if($SysError){$SBPStatus.Text = &quot;[$ComputerName] $SysError&quot;}</p> <p> &nbsp; else{</p> <p> $Info | %{</p> <p> $Item = New-Object System.Windows.Forms.ListViewItem($_.$Col0)</p> <p>$Item.BackColor = &quot;Red&quot; &nbsp;# Background color for complete Text field</p> <p>$Item.ForeColor = &quot;Yellow&quot; &nbsp;# Foreground color for complete Text field</p> <p> ForEach ($Col in ($lvMain.Columns | ?{$_.Index -ne 10})){$Field = $Col.Text;$Item.SubItems.Add($_.$Field)}</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;$lvMain.Items.Add($Item)</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}</p> <p> $SBPStatus.Text = &quot;Ready&quot;</p> <p> }</p> <p> &nbsp; &nbsp; }</p> <p>but I cant get it like I would like to.</p> <p>Maybe you could give me a helping hand on this??</p> <p>Thanks</p> <p>Fred</p>

  • <p>I am not able to open the tool in Win7</p>