Posts RSS Comments RSS 117 Posts and 170 Comments till now

Test-Host (WMI Ping -or Port Check)

I often find (specifically when using WMI) the need to ping the machine first before performing any queries. WMI takes FOREVER to timeout. I decided that I should use a script/function to test a host before passing it down the pipe.

There are a couple of problems with this approach that I had to consider.
1) How do I know what to test without corrupting the pipe or using foreach?
- For this I added a “-property” parameter that would allow the user to pick what property to check against, but still output the entire object that was inputted.

2) What about firewalls that block ping?
- Added TestPort function that does a TCP connect and returns $true or $false

3) What if I want a conditional port check.
- Added the ability to Change the Default Port for TCP Connection Test

Here is the script that I came up with and some of things it does.
Parameters
- $property: The Property to Ping or Test (Default is none.)
- $tport: The Port to test against (Default is 135. Used with -port)
- $timeout: The timeout for the connection (Default is 1000 ms, Used with -port)
- [switch]$port: Switch to Test a port instead of Ping
- [switch]$verbose: Provides Verbose Output.
Features
- Will Ping ‘$_’ by default
- Can pass the property that contains the Host to test using -property
- Can use -port to test a port instead of ping. (Uses TCP)
- Maintains the Object that is tested and passes it down the pipe if connection is passed.

A Demo of the script in action

Best Viewed Full Screen
Get the Flash Player to see this player.

Script CODE

  1. Param($property,$tport=135,$timeout=1000,[switch]$port,[switch]$verbose)
  2. Begin{
  3.     function TestPort {
  4.         Param($srv)
  5.         $error.Clear()
  6.         $ErrorActionPreference = "SilentlyContinue"
  7.         $tcpclient = new-Object system.Net.Sockets.TcpClient
  8.         $iar = $tcpclient.BeginConnect($srv,$tport,$null,$null)
  9.         $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
  10.         # Traps    
  11.         trap {if($verbose){Write-Host "General Exception"};return $false}
  12.         trap [System.Net.Sockets.SocketException]
  13.         {
  14.             if($verbose){Write-Host "Exception: $($_.exception.message)"}
  15.             return $false
  16.         }
  17.         if(!$wait)
  18.         {
  19.             $tcpclient.Close()
  20.             if($verbose){Write-Host "Connection Timeout"}
  21.             return $false
  22.         }
  23.         else
  24.         {
  25.             $tcpclient.EndConnect($iar) | out-Null
  26.             $tcpclient.Close()
  27.         }
  28.         if(!$error[0]){return $true}
  29.     }
  30.     function PingServer {
  31.         Param($MyHost)
  32.         $pingresult = Get-WmiObject win32_pingstatus -f "address=’$MyHost’"
  33.         if($pingresult.statuscode -eq 0) {$true} else {$false}
  34.     }
  35. }
  36. Process{
  37.     if($_)
  38.     {
  39.         if($port)
  40.         {
  41.             if($property)
  42.             {
  43.                 if(TestPort $_.$property){$_}  
  44.             }
  45.             else
  46.             {
  47.                 if(TestPort $_){$_}
  48.             }
  49.         }
  50.         else
  51.         {
  52.             if($property)
  53.             {
  54.                 if(PingServer $_.$property){$_}  
  55.             }
  56.             else
  57.             {
  58.                 if(PingServer $_){$_}
  59.             }
  60.         }
  61.     }
  62. }

If you want this as a function use this code

  1. function Test-Host{
  2. Param($property,$tport=135,$timeout=1000,[switch]$port,[switch]$verbose)
  3. Begin{
  4.     function TestPort {
  5.         Param($srv)
  6.         $error.Clear()
  7.         $ErrorActionPreference = "SilentlyContinue"
  8.         $tcpclient = new-Object system.Net.Sockets.TcpClient
  9.         $iar = $tcpclient.BeginConnect($srv,$tport,$null,$null)
  10.         $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
  11.         # Traps    
  12.         trap {if($verbose){Write-Host "General Exception"};return $false}
  13.         trap [System.Net.Sockets.SocketException]
  14.         {
  15.             if($verbose){Write-Host "Exception: $($_.exception.message)"}
  16.             return $false
  17.         }
  18.         if(!$wait)
  19.         {
  20.             $tcpclient.Close()
  21.             if($verbose){Write-Host "Connection Timeout"}
  22.             return $false
  23.         }
  24.         else
  25.         {
  26.             $tcpclient.EndConnect($iar) | out-Null
  27.             $tcpclient.Close()
  28.         }
  29.         if(!$error[0]){return $true}
  30.     }
  31.     function PingServer {
  32.         Param($MyHost)
  33.         $pingresult = Get-WmiObject win32_pingstatus -f "address=’$MyHost’"
  34.         if($pingresult.statuscode -eq 0) {$true} else {$false}
  35.     }
  36. }
  37. Process{
  38.     if($_)
  39.     {
  40.         if($port)
  41.         {
  42.             if($property)
  43.             {
  44.                 if(TestPort $_.$property){$_}  
  45.             }
  46.             else
  47.             {
  48.                 if(TestPort $_){$_}
  49.             }
  50.         }
  51.         else
  52.         {
  53.             if($property)
  54.             {
  55.                 if(PingServer $_.$property){$_}  
  56.             }
  57.             else
  58.             {
  59.                 if(PingServer $_){$_}
  60.             }
  61.         }
  62.     }
  63. }}

Get/Set-ADACL (ACL and SDDLs for Active Directory!)

A friend had a need to get/set Active Directory ACLs. So I wrote these.

They will use [System.DirectoryServices.ActiveDirectoryAccessRule] objects or SDDLs strings.

Note: I put the .NET classes and MS Spec for SDDLs at the bottom. Dont miss it!

Get-ADACL.ps1

  1. # Get-ADACL.ps1
  2. Param($DNPath,[switch]$SDDL,[switch]$help,[switch]$verbose)
  3. function HelpMe{
  4.     Write-Host
  5.     Write-Host " Get-ADACL.ps1:" -fore Green
  6.     Write-Host "   Gets ACL object or SDDL for AD Object"
  7.     Write-Host
  8.     Write-Host " Parameters:" -fore Green
  9.     Write-Host "   -DNPath                : Parameter: DN of Object"
  10.     Write-Host "   -sddl                  : [SWITCH]:  Output SDDL instead of ACL Object"
  11.     Write-Host "   -Verbose               : [SWITCH]:  Enables Verbose Output"
  12.     Write-Host "   -Help                  : [SWITCH]:  Displays This"
  13.     Write-Host
  14.     Write-Host " Examples:" -fore Green
  15.     Write-Host "   Get ACL for ‘cn=users,dc=corp,dc=lab’" -fore White
  16.     Write-Host "     .\Get-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’" -fore Yellow
  17.     Write-Host "   Get SDDL for ‘cn=users,dc=corp,dc=lab’" -fore White
  18.     Write-Host "     .\Get-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -sddl " -fore Yellow
  19.     Write-Host
  20. }
  21.  
  22. if(!$DNPath -or $help){HelpMe;return}
  23.  
  24. Write-Host
  25. if($verbose){$verbosepreference="continue"}
  26.  
  27. Write-Verbose " + Processing Object [$DNPath]"
  28. $DE = [ADSI]"LDAP://$DNPath"
  29.  
  30. Write-Verbose "   - Getting ACL"
  31. $acl = $DE.psbase.ObjectSecurity
  32. if($SDDL)
  33. {
  34.     Write-Verbose "   - Returning SDDL"
  35.     $acl.GetSecurityDescriptorSddlForm([System.Security.AccessControl.AccessControlSections]::All)
  36. }
  37. else
  38. {
  39.     Write-Verbose "   - Returning ACL Object [System.DirectoryServices.ActiveDirectoryAccessRule]"
  40.     $acl.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier])
  41. }

Set-ADACL.ps1

  1. # Set-ADACL.ps1
  2. Param($DNPath,$acl,$sddl,[switch]$verbose,[switch]$help)
  3. function HelpMe{
  4.     Write-Host
  5.     Write-Host " Set-ADACL.ps1:" -fore Green
  6.     Write-Host "   Sets the AD Object ACL to ‘ACL Object’ or ‘SDDL’ String"
  7.     Write-Host
  8.     Write-Host " Parameters:" -fore Green
  9.     Write-Host "   -DNPath                : Parameter: DN of Object"
  10.     Write-Host "   -ACL                   : Parameter: ACL Object"
  11.     Write-Host "   -sddl                  : Parameter: SDDL String"
  12.     Write-Host "   -Verbose               : [SWITCH]:  Enables Verbose Output"
  13.     Write-Host "   -Help                  : [SWITCH]:  Displays This"
  14.     Write-Host
  15.     Write-Host " Examples:" -fore Green
  16.     Write-Host "   Set ACL on ‘cn=users,dc=corp,dc=lab’ using ACL Object" -fore White
  17.     Write-Host "     .\Set-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -ACL $acl" -fore Yellow
  18.     Write-Host "   Set ACL on ‘cn=users,dc=corp,dc=lab’ using SDDL" -fore White
  19.     Write-Host "     .\Set-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -sddl `$mysddl" -fore Yellow
  20.     Write-Host
  21. }
  22.  
  23. if(!$DNPath -or (!$acl -and !$sddl) -or $help){HelpMe;Return}
  24.  
  25. Write-Host
  26. if($verbose){$verbosepreference="continue"}
  27. Write-Verbose " + Processing Object [$DNPath]"
  28.  
  29. $DE = [ADSI]"LDAP://$DNPath"
  30. if($sddl)
  31. {
  32.     Write-Verbose "   - Setting ACL using SDDL [$sddl]"
  33.     $DE.psbase.ObjectSecurity.SetSecurityDescriptorSddlForm($sddl)
  34. }
  35. else
  36. {
  37.     foreach($ace in $acl)
  38.     {
  39.         Write-Verbose "   - Adding Permission [$($ace.ActiveDirectoryRights)] to [$($ace.IdentityReference)]"
  40.         $DE.psbase.ObjectSecurity.SetAccessRule($ace)
  41.     }
  42. }
  43. $DE.psbase.commitchanges()
  44. Write-Host

More Info
I used the following .NET Classes
System.DirectoryServices.DirectoryEntry
http://msdn2.microsoft.com/en-us/library/system.directoryservices.directoryentry.aspx
System.DirectoryServices.ActiveDirectoryAccessRule
http://msdn2.microsoft.com/en-us/library/system.directoryservices.activedirectoryaccessrule.aspx
System.DirectoryServices.ActiveDirectorySecurity
http://msdn2.microsoft.com/en-us/library/system.directoryservices.activedirectorysecurity.aspx
System.Security.AccessControl.AccessControlSections
http://msdn2.microsoft.com/en-us/library/system.security.accesscontrol.accesscontrolsections(vs.80).aspx

SDDL Info
MS: http://msdn2.microsoft.com/en-us/library/aa379567.aspx

Get-CitrixServerLoad (The power of objects in Citrix)

I watch the forums at BrianMadden.com because I use Powershell a lot for Citrix. This question was brought up.

Q: How could one:
- query “server load” on all servers part of the farm
- extract all server under a minimum server load
- apply an “Offline” load evaluator on the extracted servers (in order to make them unavailable on the farm)

I posted a script to do what they wanted, but then I got to thinking… while it did achieve the goal it wasn’t very Powershellish.

As I have said over and over. The glory of Powershell is the objects. So I decided to Post this entry showing what I would consider the Powershell way :)
Ideally you should just do this at the prompt

PS> Get-CitrixServers | where{$_.WinServerObject.Serverload -lt $load} | Set-CitrixLoadEvalutor “OffLine”

This is easy to achieve with the following scripts or even better make them functions!

Get-CitrixServers

  1. param($Server)
  2. $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
  3. $mfarm = [system.Activator]::CreateInstance($type)
  4. $mfarm.Initialize(1)
  5. $mfarm.zones | foreach-Object{$_.OnlineServers}

Set-CitrixLoadEvalutor

  1. Param($server,$LoadEvaluator = "MFDefaultLE",[switch]$Verbose)
  2. #NOTE: This only work for 4.0 and 4.5
  3. if($verbose){$verbosepreference = "Continue"}
  4.  
  5. function Set-LE{
  6.     Param($mySrv)
  7.     # Getting Current LE
  8.     write-Verbose "   + Set-LE called : $($mySrv.ServerName)"
  9.     $le = $mfServer.AttachedLE
  10.     $le.LoadData(1)
  11.     Write-Verbose "     - Old Evaluator: $($le.LEName)"
  12.     Write-Verbose "     - Setting to $LoadEvaluator"
  13.  
  14.     # Assigning New LE
  15.     $mySrv.AttachLEByName($LoadEvaluator)
  16.  
  17.     # Checking LE
  18.     $le = $mySrv.AttachedLE
  19.     $le.LoadData(1)
  20.     Write-Verbose "     - Load Evaluator Set to $($le.LEName)"
  21.  
  22. }
  23.  
  24. if($Server)
  25. {
  26.     # Loading Server Object
  27.     Write-Verbose " + Processing $Server"
  28.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
  29.     $mfServer = [system.Activator]::CreateInstance($type)
  30.     $mfServer.Initialize(6,$Server)
  31.     Write-Verbose "   - Calling Set-LE"
  32.     Set-LE $mfServer
  33. }
  34.  
  35. if($list)
  36. {
  37.     foreach($Srv in (Get-Content $list))
  38.     {
  39.         Write-Verbose " + Processing $Srv"
  40.         # Loading Server Object
  41.         Write-Verbose "   - Getting Citrix Object"
  42.         $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Srv)
  43.         $mfServer = [system.Activator]::CreateInstance($type)
  44.         $mfServer.Initialize(6,$Srv)
  45.         Write-Verbose "   - Calling Set-LE"
  46.         Set-LE $mfServer
  47.     }
  48. }
  49.  
  50. if($input)
  51. {
  52.     foreach($Srv in $input)
  53.     {
  54.         Write-Verbose     " + Processing $Srv"
  55.         if($Srv.ServerName)
  56.         {
  57.             Write-Verbose "   - Input is a Citrix Server: $Srv"
  58.             Write-Verbose "   - Calling Set-LE"
  59.             Set-LE $Srv
  60.         }
  61.         else
  62.         {
  63.             Write-Verbose "   - Input: $Srv"
  64.             # Loading Server Object
  65.             Write-Verbose "   - Getting Citrix Object"
  66.             $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Srv)
  67.             $mfServer = [system.Activator]::CreateInstance($type)
  68.             $mfServer.Initialize(6,$Srv)
  69.             Write-Verbose "   - Calling Set-LE"
  70.             Set-LE $mfServer
  71.         }
  72.     }
  73. }

This was the all in one that I posted

  1. Param($Server,$minLoad = 1000,$LoadEval,[switch]$verbose)
  2. if($verbose){$verbosepreference = "continue"}
  3. function Get-CitrixFarm{
  4.     param($Srv)
  5.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Srv)
  6.     $mfarm = [system.Activator]::CreateInstance($type)
  7.     $mfarm.Initialize(1)
  8.     Write-Verbose "Loading Farm $($mFarm.FarmName)"
  9.     return $mFarm
  10. }
  11. function Set-CitrixLoadEvalutor{
  12.     Param($server,$LoadEvaluator = "MFDefaultLE")
  13.  
  14.     # Loading Server Object
  15.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
  16.     $mfServer = [system.Activator]::CreateInstance($type)
  17.     $mfServer.Initialize(6,$Server)
  18.  
  19.     # Getting Current LE
  20.     $le = $mfServer.AttachedLE
  21.     $le.LoadData(1)
  22.     Write-Verbose "Old Evaluator: $($le.LEName)"
  23.     Write-Verbose "Setting Load Evaluator on $server to $LoadEvaluator"
  24.  
  25.     # Assigning New LE
  26.     $mfServer.AttachLEByName($LoadEvaluator)
  27.  
  28.     # Checking LE
  29.     $le = $mfServer.AttachedLE
  30.     $le.LoadData(1)
  31.     Write-Verbose "Load Evaluator Set to $($le.LEName)"
  32. }
  33.  
  34. $farm = Get-CitrixFarm $Server
  35. foreach($ctxServer in $farm.Servers)
  36. {
  37.     $load = $ctxServer.WinServerObject.Serverload
  38.     Write-Host ("{0,-15} :: {1}" -f $ctxServer.ServerName,$load)
  39.     if($load -lt $minLoad)
  40.     {
  41.         Write-Verbose "Setting Offline Load Eval"
  42.         if($LoadEval){Set-CitrixLoadEvalutor $ctxServer.ServerName $LoadEval}
  43.     }
  44. }

Get-CitrixHotfix: The Bitter/Sweet of Write-Verbose

There are a whole host of of Write-* Cmdlets.

Write-Debug
Write-Error
Write-Host
Write-Output
Write-Progress
Write-Verbose
Write-Warning

Each one of these are useful, but I want to specifically talk about Write-Verbose. Note, Write-Debug work basically the same. Also, for those of you that follow the Powershell Podcast over at http://powerscripting.wordpress.com some of this was covered already in Episode 11.

The way Write-Verbose works is that it uses $VerbosePreference to determine what to do. This is very useful because it gives you the ability to easily control if the string is written to the host. I think this is a good time to clarify that write-verbose only writes to the host and does NOT pollute the output stream which is SUPER useful.

Lets point out the goods and bads

Goods
1) Can use a switch Parameter to easily control the console info (I normally use $verbose)
2) It writes to host so you dont pollute the object output
3) Nice for writing Data to the console on scripts that take a long time to run

Bads
1) It has a header on every line that cannot be removed “VERBOSE:”
2) It is Yellow (eek!)

The good news is you can control the color using $host.PrivateData
More Info http://ps1.soapyfrog.com/2007/01/29/debug-and-verbose-colouring/

The following is a script that I wrote that collects Citrix Hotfixes. I have a large number of servers so I wanted to be able see where I was and I also wanted a overview of the hotfixes, but I also wanted to output and object for filtering purposes. In this case Write-Verbose was perfect. I was able to write to the screen (host) what I wanted to see without changing the object output. I was also able to control whether it was outputed by using a switch parameter to control $VerbosePreference.

  1. Param($Server,$log,[switch]$Farm,[switch]$Verbose,[switch]$debug)
  2. # Get-CitrixHotfix.ps1
  3.  
  4. if($Verbose){$VerbosePreference = "Continue"}
  5. if($debug){Set-PSDebug -Step}
  6.  
  7. Write-Host "`nProcessing…`n"
  8.  
  9. if($Farm)
  10. {
  11.     # Get Farm Object
  12.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
  13.     $CTXFarm = [system.Activator]::CreateInstance($type)
  14.     $CTXFarm.Initialize(1)
  15.  
  16.     # Creating Collection for Custom Objects
  17.     $myCol = @()
  18.  
  19.     foreach($Srv in $CTXFarm.Servers)
  20.     {
  21.         # Create Custom Object
  22.         $myobj = "" | Select-Object Name,Hotfix
  23.         $myobj.Name = $Srv.ServerName
  24.         $myobj.Hotfix = @()
  25.  
  26.         Write-Verbose $Srv.ServerName
  27.  
  28.         # Get Hotfix Information for the Server and add to Custom Object
  29.         $CTXServer = $CTXFarm.GetServer2(6,$Srv.ServerName)
  30.         $CTXServer.winServerObject2.hotfixes | %{Write-Verbose " - $($_.Name)";$myobj.HotFix += $_.name}
  31.  
  32.         # Add Server Object to Collection
  33.         $myCol += $myobj
  34.     }
  35.     # Output Collection
  36.     if($log)
  37.     {
  38.         @(foreach($obj in $mycol)
  39.         {
  40.             Write-Output $obj.Name
  41.             foreach($hf in $obj.Hotfix){write-Output " - $hf"}
  42.         }) | out-File $log -enc ASCII
  43.     }
  44.     else
  45.     {
  46.         $mycol
  47.     }
  48. }
  49. else
  50. {
  51.     $myobj = "" | Select-Object Name,HotFix
  52.     $myobj.Name = $Server
  53.     $myobj.HotFix = @()
  54.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
  55.     $mfServer = [system.Activator]::CreateInstance($type)
  56.     $mfServer.Initialize(6,$Server)
  57.     Write-Output $Server
  58.     $mfServer.winServerObject2.hotfixes | foreach-Object{$myobj.HotFix += $_.Name}
  59.     $myobj
  60. }
  61.  
  62. write-host
  63.  
  64. if($debug){Set-PSDebug -off}