Hey, Scripting Guy! Quick-Hits Friday: The Scripting Guys Respond to a Bunch of Questions (1/29/10)

ScriptingGuy1

Bookmark and ShareIn this post:

 

How Can I Run This Windows PowerShell Command on a Remote Computer?

Hey, Scripting Guy! Question

Hey, Scripting Guy!

I wanted to run the DU tool on several servers in my environment. Is there a way to script this request via Windows PowerShell? I can run it for one server by doing something like this.

PS> du -v “z:windows” | sort -Descending | Out-File c:du_test.txt

The Z: drive represents a mapping of the target server C: drive.

— JC

 

Hey, Scripting Guy! AnswerHello JC,

Microsoft Scripting Guy Ed Wilson here. I would use Windows PowerShell 2.0 and remote the command. It is the easiest way to do this. You can download Windows PowerShell 2.0 for Windows Server 2008, Windows Vista, Windows XP, and Windows Server 2003. For Windows 7 and Windows Server 2008 R2, there is no need to download Windows PowerShell 2.0 because it is built into the operating system.

After you have installed Windows PowerShell 2.0, take a look at the about_Remoting articles in the online help. I access the help files by using the Get-Help cmdlet (help is an alias for that cmdlet), as seen here:

PS C:> help about_remote*

Name                              Category  Synopsis
—-                              ——–  ——–
about_remote                      HelpFile  Describes how to run remote commands in Windows PowerShell.
about_remote_FAQ                  HelpFile  Contains questions and answers about running remote commands
about_remote_jobs                 HelpFile  Describes how to run background jobs on remote computers.
about_remote_output               HelpFile  Describes how to interpret and format the output of remote commands.
about_remote_requirements         HelpFile  Describes the system requirements and configuration requirements for
about_remote_troubleshooting      HelpFile  Describes how to troubleshoot remote operations in Windows PowerShell.

PS C:>

To run a command on a remote machine, you can use the Invoke-Command cmdlet, as shown here:

PS C:> Invoke-Command -ComputerName win7-pc -ScriptBlock { ipconfig }

Windows IP Configuration

Ethernet adapter Local Area Connection 2:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::dc70:f882:bda5:9133%17
   IPv4 Address. . . . . . . . . . . : 192.168.1.110
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.1.254

Tunnel adapter isatap.{468C0E87-CED5-4D23-9F0E-C1B99C508131}:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :

Tunnel adapter Local Area Connection* 11:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
PS C:>

Executing against multiple machines is just as easy. If you have several computer names, you may want to place them into a text file that is readily accessible. This file needs to be accessible from the computer from where you launch the script—it does not need to be accessible on the target computers. A simple computers.txt file is seen in the following image.

Image of computers.txt file

By using the Get-Content cmdlet to read the contents of the text file to obtain the values for the –computername parameter, you can easily execute the command on a group of remote servers. This is seen here:

PS C:> Invoke-Command -ComputerName (Get-Content C:fsocomptuers.txt) -ScriptBlock {ipconfig}

Windows IP Configuration

Ethernet adapter Local Area Connection 2:

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::dc70:f882:bda5:9133%17
   IPv4 Address. . . . . . . . . . . : 192.168.1.110
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.1.254

Tunnel adapter isatap.{468C0E87-CED5-4D23-9F0E-C1B99C508131}:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :

Tunnel adapter Local Area Connection* 11:

   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :

Windows IP Configuration

Ethernet adapter Local Area Connection 3:

   Connection-specific DNS Suffix  . : nwtraders.com
   Link-local IPv6 Address . . . . . : fe80::ec91:63e2:e2c9:3692%414
   IPv4 Address. . . . . . . . . . . : 192.168.1.100
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.1.254
PS C:>

If for some reason you cannot upgrade to Windows PowerShell 2.0 at this time, your options will be somewhat limited. You may be able to use WMI and the create method from win32_process to run the remote command. The syntax is a bit nasty, as seen here:

 ([wmiclass]”\COMPUTERNAMErootcimv2:win32_process“).create(“PROCESSNAME”)


 

Troubleshooting a Windows PowerShell Script for Active Directory 

Hey, Scripting Guy! Question

Hey, Scripting Guy!

I’ve tried to use Windows Powershell 2.0’s Active Directory cmdlets to create a list of orphaned home drives as per the below; however, it seems that the Get-ADUser query always returns $null for all users with a samaccountname starting with 0-9 or A-F, even though doing a “Get-ADUser ‘username’ –Properties HomeDirectory” for one of those users returns a non-null value. Anyone with a samaccountname starting with G-Z returns the correct value; this happens whether it is run as part of this script or as one line against one of the accounts. Is there some quirk that I am unaware of or is there something seriously wrong with our domain? If it is relevant, our domain is 2008 Native with a mix of 2008 and 2008 R2 domain controllers, both RW and RO.

import-module ActiveDirectory
 
$path = “
\pathtousers
$file = New-Item -type file “C:unusedhome.txt”
 
 
$homedrives = Get-ChildItem $path
 
ForEach($folder in $homedrives){
 
$name = $path + $
folder.name
 
$user = Get-ADUser -Filter { HomeDirectory -eq $name }
 
if($user -eq $null){
add-content $file $name
}
 
$user = $null
 
}

Many thanks.

— AB

 

Hey, Scripting Guy! AnswerHello AB,

Microsoft MVP Brandon answered this one. He said, “Try this, it works for me” about this script, which he wrote:

import-module ActiveDirectory

$path = “C:dataHomeFolder”
$file = New-Item -type file “C:dataunusedhome.txt” -Force
 
$homedrives = Get-ChildItem $path
 
foreach($folder in $homedrives)
{
      $user = Get-ADUser $Folder.Name  -properties HomeDirectory
      Write-Host “Found User: $User”
      if($user.homedirectory -ne $folder.FullName){$folder.FullName | add-content $file.FullName}
      $user = $null
}


  < p style=”MARGIN: 0in 0in 8pt” class=”MsoNormal”> Can I Detect the Title of the Command Prompt Window? Hey, Scripting Guy! Question

Hey, Scripting Guy!

I want to detect the command prompt window title. I need to be able to determine if the user is running in an elevated/admin prompt or not. The window title for a “normal” Command Prompt windows says, “Command Prompt,” but the window title for an admin Command Prompt windows says, “Administrator: Command Prompt.”

If I store the title so I can compare against the two window titles above, I can determine if I am just in a normal shell or an elevated shell and then take actions based on where the user is. I was hoping to use ordinary command-prompt utilities, but I do know VBScript, I have not looked very deep into Windows PowerShell yet.

However, you could restate the task as “determining if a user is in an elevated prompt.” It does not matter to me if the window title is used to figure that out. It is just the only way I can see at the moment that they are different. If there is, another way to do this is okay, too.

Hope you can help.

— KW

 

Hey, Scripting Guy! AnswerHello KW,

Take a look at this script. It uses Windows PowerShell 2.0 (required for the TryCatch bit).

Test-CmdPromptAdmin.ps1

# ——————————————————————–
# Test-CmdPromptAdmin.ps1
# ed wilson, msft, 12/22/2009
#
# Checks to see if the cmd prompt is elevated.
# If no cmd prompt then script exits.
#
# ——————————————————————–
Function Test-CmdPromptAdmin
{
 $errorActionPreference = “stop”
 Try
  {
   $windowTitle = (get-Process -Name cmd).MainWindowTitle
   if($windowTitle -match “administrator”)
     { $true }
   else { $false }
  }
 Catch [System.Exception]
  { exit }
} #end function Test-CmdPromptAdmin

# *** Entry point to script ***

If(Test-CmdPromptAdmin)
  { “The cmd prompt is elevated”}
If(-not (Test-CmdPromptAdmin))
  { “The cmd prompt is not elevated” }


 

How Can I Add Quotation Marks Around a String Variable? Hey, Scripting Guy! Question

Hey, Scripting Guy! 

Hey, Scripting Guy! I would like to add quotation marks around a string variable. However, for whatever reason, the first quotation mark is fine but the second quotation mark goes to the next line. Any ideas about how to fix this? Here is all I am doing:

PS C:> $test='”‘+”$strname”+'”‘

PS C:> $test

“\?Volume{747b409b-b755-11de-ab54-0025b3a915b7}

  

As you can see, the second quotation mark is not where I want it to be. I want it to be after the last “”.

— OC

 

Hey, Scripting Guy! AnswerHello OC,

My guess is that it is exceeding your screen width. The script should still run fine.

In the Windows PowerShell console, you can increase the screen width by clicking the Windows Powershell icon in the upper left corner, clicking Properties, and then clicking Layout. The width is by default set to 80; I generally set mine to 120. This is seen in the following image.

Image of increasing screen width

 

Well, this concludes another edition of Quick-Hits Friday. It also concludes another exciting week on the Script Center. Join us next week as we delve into the mysteries