Posts RSS Comments RSS 117 Posts and 170 Comments till now

Archive for October, 2007

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}

Error Handling: Part3 - The Power of Set-PSDebug

From a scripting perspective this CMDLet is one of the greatest tools you could possibly have access to. The guys over at the Powershell Podcast also did a quick overview in Latest Podcast at http://powerscripting.net

Lets start with an overview of the CMDLet. A good portion of these will be covered in the help file (PS> get-help Set-PSDebug -full) So I highly recommed reviewing that documentation as well.
-trace
    0 - turn off tracing
    1 - trace script lines as they are executed
    2 - trace basically everything
-step: Will prompt you for action
    [Y] Yes (Default): Will Process the line and prompt again at the next.
    [A] Yes to All: Will process all lines, Usually used after you Caught what you were looking for.
    [N] No: Will STOP processing on all lines, effectively terminating the script.
    [L] No to All: Will STOP processing on all lines, effectively terminating the script.
    [S] Suspend: This option is awesome. This will open another runspace where you can inspect the
         environment from the shell. Basically, if you want to figure out what the state of a variable or object
         is at a given time. You can just Suspend and get the information or change it.
         Type exit goes to the next prompt.

  1. Set-PSDebug -step
  2. Continue with this operation?
  3.    1+ C:\tools\Testme.ps1 hello
  4. [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
  5. DEBUG:    1+ C:\tools\Testme.ps1 hello
  6.  
  7. Continue with this operation?
  8.    2+ $myhi = $hi
  9. [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
  10. DEBUG:    2+ $myhi = $hi
  11.  
  12. Continue with this operation?
  13.    3+ Write-Host "$Myhi was passed" -fore Green
  14. [Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "Y"):
  15. DEBUG:    3+ Write-Host "$Myhi was passed" -fore Green
  16. hello was passed

    Here are some gotcha’s for Stepping
    - “[A] Yes to All” effectively turns off stepping
    - “[N] No” and “[L] No to All” both do the same thing… Kill the processing.
    - -step Automatically sets debug trace to level 1

-off: Turns off Debugging

-strict: Variable must be assigned a value before being referenced (Kind of like Option Explict in Vbscript.)

  1. Set-PSDebug -strict
  2. $myvar                     # This line will throw an error
  3. $myvar = "Hi There"
  4. $myvar                     # This line will output "Hi There"

What does the Debug output look like?

Here is the script we will be debugging

  1. Param($hi)
  2. function foo{
  3.    Param($iFoo)
  4.    Return "$iFoo was Passed"
  5. }
  6. $myvar = foo $hi
  7. Write-Host $myvar

Here is the script with Set-PSDebug -Trace 1

  1. PS: Set-PSDebug -Trace 1
  2. PS: C:\tools\Testme.ps1 Hi
  3. DEBUG:    1+ C:\tools\Testme.ps1 Hi
  4. DEBUG:    2+ function foo{
  5. DEBUG:    6+ $myvar = foo $hi
  6. DEBUG:    4+    Return "$iFoo was Passed"
  7. DEBUG:    7+ Write-Host $myvar
  8. Hi was Passed

Things to Notice
- Line numbers are referenced in the order they were called
- The Debug info is Prefaced with “DEBUG:”
- Stepping is not used by default

Here is the script with Set-PSDebug -Trace 2

  1. PS: Set-PSDebug -Trace 2
  2. PS: C:\tools\Testme.ps1 Hi
  3. DEBUG:    1+ C:\tools\Testme.ps1 Hi
  4. DEBUG:     ! CALL script ‘Testme.ps1′
  5. DEBUG:    2+ function foo{
  6. DEBUG:    6+ $myvar = foo $hi
  7. DEBUG:     ! CALL function ‘foo’  (defined in file ‘C:\tools\Testme.ps1′)
  8. DEBUG:    4+    Return "$iFoo was Passed"
  9. DEBUG:     ! SET $myvar = ‘Hi was Passed’.
  10. DEBUG:    7+ Write-Host $myvar
  11. Hi was Passed

Things to Notice
- You Now see calls to scripts, functions and variable assignments
- Calls to Scripts, Functions, and Setting Variable are prefaced with !
- Calls to function tell you where they came from

Summary:
I hope this post does justice to Set-PSDebug. It is extremely useful and powerful for writing scripts. I truly hope you take some time to play with this CMDLet and see where it takes you. It has done wonders for me.

Bonus:
Wanna see how the evironment effects a CMDLet or even just a little peak under the hood? Use Set-PSDebug -trace 2

  1. 69# Set-PSDebug -trace 2
  2. DEBUG:    1+ Set-PSDebug -trace 2
  3. 70# Get-ChildItem c:\fes
  4. DEBUG:    1+ Get-ChildItem c:\fes
  5. DEBUG:    2+                                     if ($ErrorView -ne "CategoryView") {
  6. DEBUG:    3+                                        $myinv = $_.InvocationInfo
  7. DEBUG:     ! SET $myinv = ‘System.Management.Automation.InvocationInfo’.
  8. DEBUG:    4+                                        switch -regex ($myinv.MyCommand.CommandType)
  9. DEBUG:   24+                                                if ($myinv.MyCommand.Name)
  10. DEBUG:   26+                                                    $myinv.MyCommand.Name + " : "; break;
  11. DEBUG:   26+                                                    $myinv.MyCommand.Name + " : "; break;
  12. DEBUG:    2+                                     if ($_.InvocationInfo) {
  13. DEBUG:    3+                                         $posmsg = $_.InvocationInfo.PositionMessage
  14. DEBUG:     ! SET $posmsg =
  15. At line:1 char:14
  16. + Get-ChildItem  <<<< c:\fes’.
  17. DEBUG:    7+                                     if ($ErrorView -eq "CategoryView") {
  18. DEBUG:   11+                                         $_.Exception.Message + $posmsg
  19. Get-ChildItem : Cannot find path ‘C:\fes’ because it does not exist.
  20. At line:1 char:14
  21. + Get-ChildItem  &lt;&lt;&lt;&lt; c:\fes

Error Handling: Part2 - Throw “A word about Trapping”

I was writing a post on this, but Joel (Jaykul) beat me to the punch (he did a great job to.)

http://huddledmasses.org/trap-exception-in-powershell

Error Handling: Part1 - $Error Objects

Error handling is one thing that is not discussed as much as it should be. It is one the most important parts of working with any scripting language and in this case the interactive nature of Powershell. We have had much discussion in the IRC channel over the last couple of weeks, discussing this exact topic. So I thought I should do my best to post what I have gleaned from the discussion.

This is one of at least a two part (maybe Three) series.
* Error Handling: Part1 - $Error Objects
* Error Handling: Part2 - Throw “A word about Trapping”
* Error Handling: Part3 - The Power of Set-PSDebug

Ask anyone that writes scripts. The ratio for Error correction in code is INSANE. Its almost 2/1. A 10 line script soon becomes 30 and a simple script becomes insanely complicated add to this the interactive nature of Powershell and you have the potential for many headaches. While Error handling in Powershell is far superior than anything to date… it still has some qwerks. I will do my best to cover some of these. Before we proceed from here, It is VERY important that you understand scope. I started to write this up, but to be honest, I think the Powershell team did a great job here. Please take some time to read the about file for scope.
PS> get-help about_Scope

Now, lets start off with some of the basics:

There are several built-in objects in powershell that control and provide information about Errors
$Error
> This is a object that stores an array of RICH Error objects
> Type: System.Management.Automation.ErrorRecord
$MaximumErrorCount:
> Total number of ErrorRecord objects that will be saved in $error
$LASTEXITCODE
> Exit Code of the Last script or native command than ran (i.e. ping.exe)
> This does NOT include cmdlets
$?
> Boolean Value that is $false if an error occured on the last command.
$ErrorActionPreference
> Determines the Default action if error is thrown
> Type System.Management.Automation.ActionPreference
> Continue: Outputs error, but keeps processing
> SilentlyContinue: No output and it keeps going
> Inquire: Prompt user for action
> Stop: Outputs error and halts processing
$ErrorView
> This is how the error will output to the screen
> This truly is a variable and can contain anything, but will use NormalView if anything other than CategoryView is the value
> NormalView: Default. Outputs semi-verbose description,Line Number, and character
> CategoryView: Only displayes the category the error is in.

Now that we have an overview of the objects that make up the error environment in Powershell lets deep dive into a couple of the more important ones.

$Error: This is by far the object with the most amount of data… it really does contain pretty much everything you could possibly want to know about an error. It is important to realize that this is truly an object. It has properties, methods, and even a Type just as all the other objects have. You can see this by piping $error to get-member

  1. PS&gt; $error | get-member
  2.    TypeName: System.Management.Automation.ErrorRecord
  3.  
  4. Name                      MemberType Definition
  5. ====                      ========   ========
  6. Equals                    Method     System.Boolean Equals(Object obj)
  7. GetHashCode               Method     System.Int32 GetHashCode()
  8. GetObjectData             Method     System.Void GetObjectData(SerializationInfo info, StreamingContext context)
  9. GetType                   Method     System.Type GetType()
  10. get_CategoryInfo          Method     System.Management.Automation.ErrorCategoryInfo get_CategoryInfo()
  11. get_ErrorDetails          Method     System.Management.Automation.ErrorDetails get_ErrorDetails()
  12. get_Exception             Method     System.Exception get_Exception()
  13. get_FullyQualifiedErrorId Method     System.String get_FullyQualifiedErrorId()
  14. get_InvocationInfo        Method     System.Management.Automation.InvocationInfo get_InvocationInfo()
  15. get_TargetObject          Method     System.Object get_TargetObject()
  16. set_ErrorDetails          Method     System.Void set_ErrorDetails(ErrorDetails value)
  17. ToString                  Method     System.String ToString()
  18. CategoryInfo              Property   System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}
  19. ErrorDetails              Property   System.Management.Automation.ErrorDetails ErrorDetails {get;set;}
  20. Exception                 Property   System.Exception Exception {get;}
  21. FullyQualifiedErrorId     Property   System.String FullyQualifiedErrorId {get;}
  22. InvocationInfo            Property   System.Management.Automation.InvocationInfo InvocationInfo {get;}
  23. TargetObject              Property   System.Object TargetObject {get;}

Note: Because $error is an array it inherits from [System.Array] and its methods. Clear() is the most notable.

I could probably do a whole post on just $error, but that is beyond the scope of this one so I want just cover a few properties and one method
ErrorDetails
> This provides the Details of the Error. I wanted to note this is often blank
Exception
> This is the actual exception that was thrown
> This is an object so it will have properties and methods of its own.
TargetObject
> This is the object that operation was being performed when the error occured.
InvocationInfo
> This contains detail information about the command that was run (i.e. Line Number, CMDlet, and Position)
Clear()
> This method clears the $error array.

More Info: http://msdn2.microsoft.com/en-us/library/system.management.automation.errorrecord.aspx

$ErrorActionPreference: I would say this is the second most important object for error handling. I have personally seen this question posted at least 10 times on the News Groups, “What is the powershell equivalent to “On Error Resume Next.” The answer lies in this object more specifically by setting $ErrorActionPreference to “SilentlyContinue.” One thing I would like to point out about this particular object is that it is scope specific. This is very important to understand. Learning to control error action at the lowest level is key to writing good scripts and knowing the state of this object can save you a ton of heartache. I cant tell you how many times I beat my head against the wall trying to figure out why I wasnt getting any error information… hmmm, $erroractionpreference set to “SilentlyContinue,” maybe that is the problem.

More Info Here: http://msdn2.microsoft.com/en-us/library/system.management.automation.actionpreference.aspx

In Summary, I think this a good overview of the Error Variables/Objects that you have access to, but it really only scratches the surface. I would suggest spending some time on MSDN looking through system.management.automation Namespace and get a good idea of all the information there.

Citrix Load Evaluators

I have had a few request for how to deal with Citrix Load Evaluators. There are few gotchas, but it is fairly strait forward.

There are two Built-in Citrix Load Evaluators Default and Advanced.. the problem is that from MFCom, this is not what they are called. This is what you should use.
- Default = MFDefaultLE
- Advanced = LMSDefaultLE

  1. function Set-CitrixLoadEvaluator{
  2.     Param($server = $(throw ‘$Server is Required’),$LoadEvaluator = "MFDefaultLE")
  3.  
  4.     # Loading Server Object
  5.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
  6.     $mfServer = [system.Activator]::CreateInstance($type)
  7.     $mfServer.Initialize(6,$Server)
  8.  
  9.     # Getting Current LE
  10.     $le = $mfServer.AttachedLE
  11.     $le.LoadData(1)
  12.     Write-Host "Old Evaluator: $($le.LEName)"
  13.     Write-Host "Setting Load Evaluator on $server to $LoadEvaluator"
  14.  
  15.     # Assigning New LE
  16.     $mfServer.AttachLEByName($LoadEvaluator)
  17.  
  18.     # Checking LE
  19.     $le = $mfServer.AttachedLE
  20.     $le.LoadData(1)
  21.     Write-Host "Load Evaluator Set to $($le.LEName)"
  22. }

Citrix functions Updated!

I didn’t have time last post to additional “Farm” functionality to some the functions. The following are updated to work against multiple farms.

Updated Server functions

  • Publish-CitrixApplication
  • UnPublish-CitrixServer
  • Remove-CitrixApplication
  • Updated Application functions

  • Get-CitrixApp
  • Get-CitrixAppUsers
  • Get-CitrixAppServers
  • Find-CitrixUser
  • Citrix Server Functions

    1. ##########################################
    2. ####     Citrix Server Functions      ####
    3. ##########################################
    4.  
    5. ## Publish Application to Server(s)
    6. ## -app: Name of Application to remove. This is required
    7. ## -Server: Name of Server
    8. ## PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    9. ## NOTE: App Name must include subfolders of the app.
    10. ##       If the app in in Applications\Test then app would be Test\MyApp
    11. ##       Example: Publish-CitrixApplication -server myserver -app Test\MyApp
    12. function Publish-CitrixApplication{
    13.     Param([string]$server,[string]$app)
    14.     Begin{
    15.         Write-Host
    16.         function gcs{
    17.             Param($srv)
    18.             $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
    19.             $mfServer = [system.Activator]::CreateInstance($type)
    20.             $mfServer.Initialize(6,$Server)
    21.             $mfServer
    22.         }
    23.         function gca{
    24.             Param($srv,$ma)
    25.             $type = [System.Type]::GetTypeFromProgID("MetaFrameCOM.MetaFrameApplication",$Server)
    26.             $mfApp = [system.Activator]::CreateInstance($type)
    27.             $mfApp.Initialize(3,"Applications\$ma")
    28.             $mfApp.LoadData($true)
    29.             $mfApp
    30.         }
    31.         function cPublish {
    32.             Param([string]$Srv,[string]$myapp)
    33.             $Srv = $Srv.toUpper()
    34.             $mfSrv = gcs $srv
    35.             $mfApp = gca $srv $myapp
    36.             $mfAppBinding = New-Object -ComObject MetaFrameCOM.MetaFrameAppSrvBinding
    37.             $mfAppBinding.Initialize(6,$Srv,"Applications\$app")
    38.             if($mfAppBinding)
    39.             {
    40.                 Write-Host "Publishing App[$myapp] on Server [$Srv]" -ForegroundColor Green
    41.                 $mfApp.AddServer($mfAppBinding)
    42.                 $mfApp.SaveData()
    43.             }
    44.             else
    45.             {
    46.                 Write-Host "Unable To Create App Binding" -ForegroundColor Red
    47.             }
    48.         }
    49.         $process = @()
    50.     }
    51.     Process{
    52.         if($_){
    53.             if($_.ServerName){
    54.                 $process += $_.ServerName
    55.             }
    56.             else{
    57.                 $process += $_
    58.             }
    59.         }
    60.     }
    61.     End{
    62.         if($Server){$Process += $Server}
    63.         foreach($s in $process){
    64.             cPublish -srv $s -myapp $app
    65.             Write-Host
    66.         }
    67.     }
    68. }
    69.  
    70. ## UnPublishes ALL Application from Server(s)
    71. ## -Server: Name of Server
    72. ## PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    73. function UnPublish-CitrixServer{
    74.     Param([string]$server)
    75.     Begin{
    76.         function gcs{
    77.             Param($srv)
    78.             $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
    79.             $mfServer = [system.Activator]::CreateInstance($type)
    80.             $mfServer.Initialize(6,$Server)
    81.             $mfServer
    82.         }
    83.         function cUnPublish {
    84.             Param([string]$Srv)
    85.             $Srv = $Srv.toUpper()
    86.             $mfSrv = gcs $srv
    87.             If($mfSrv.Applications.Count -gt 0)
    88.             {
    89.                 Write-Host "Removing All Published Applications from $Srv" -ForegroundColor Red
    90.                 Write-Host "===================================================" -ForegroundColor Green
    91.                 ForEach($a in $mfSrv.Applications)
    92.                 {
    93.                     $myApp = $a.AppName
    94.                     Write-Host "Removing App [$myApp] from Server [$Srv]" -ForegroundColor White
    95.                     $a.RemoveServer($Srv)
    96.                     $a.SaveData()
    97.                 }
    98.             }
    99.             else
    100.             {
    101.                 Write-Host "No Published Applications for $Srv" -ForegroundColor Red
    102.             }
    103.         }
    104.         Write-Host
    105.         $process = @()
    106.     }
    107.     Process{
    108.         if($_){
    109.             if($_.ServerName)
    110.             {
    111.                 $process += $_.ServerName
    112.             }
    113.             else
    114.             {
    115.                 $process += $_
    116.             }
    117.         }
    118.     }
    119.     End{
    120.         if($Server){$Process += $Server}
    121.         foreach($s in $process){
    122.             cUnPublish $s
    123.             Write-Host
    124.         }
    125.     }
    126. }
    127.  
    128. ## Remove Specific Application from Server(s)
    129. ## -app: Name of Application to remove. This is required
    130. ## -Server: Name Server.
    131. ## PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    132. function Remove-CitrixApplication {
    133.     Param([string]$server,[string]$app)
    134.     Begin{
    135.         function gcs{
    136.             Param($srv)
    137.             $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
    138.             $mfServer = [system.Activator]::CreateInstance($type)
    139.             $mfServer.Initialize(6,$Server)
    140.             $mfServer
    141.         }
    142.         function RemoveApp {
    143.             Param([string]$Srv,[string]$myapp)
    144.             $AppRemoved = $false
    145.             $Srv = $Srv.toUpper()
    146.             $mfSrv = gcs $srv
    147.             If($mfSrv.Applications.Count -gt 0)
    148.             {
    149.                 ForEach($a in $mfSrv.Applications)
    150.                 {
    151.                     If(($a.AppName -eq "$myapp") -or ($a.BrowserName -eq "$myapp"))
    152.                     {
    153.                         Write-Host "Removing App [$myApp] from Server [$Srv]" -ForegroundColor Green
    154.                         $a.RemoveServer($Srv)
    155.                         $a.SaveData()
    156.                         $AppRemoved = $true
    157.                     }
    158.                 }
    159.             }
    160.             else
    161.             {
    162.                 Write-Host "No Applications Published for $Srv" -ForegroundColor Red
    163.                 $AppRemoved = $true
    164.             }
    165.             If($AppRemoved -eq $false)
    166.             {
    167.                 Write-Host "This Application not Published for $Srv" -ForegroundColor Red
    168.             }
    169.         }
    170.         Write-Host
    171.         $process = @()
    172.     }
    173.     Process{
    174.         if($_)
    175.         {
    176.             if($_.ServerName){
    177.  
    178.                 $process += $_.ServerName
    179.             }
    180.             else
    181.             {
    182.                 $process += $_
    183.             }
    184.         }
    185.     }
    186.     End{
    187.         if($Server){$Process += $Server}
    188.         if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
    189.         foreach($s in $process)
    190.         {
    191.             RemoveApp -Srv $s -myapp $app
    192.             Write-Host
    193.         }
    194.     }
    195. }

    Citrix App Functions

    1. #########################################
    2. ####     Citrix App Functions        ####
    3. #########################################
    4. ## Returns Citrix Application for Farm
    5. ## -Server: Name of Farm Server. This is required
    6. ## -App: Name of Application to remove. This is required
    7. function Get-CitrixApp{
    8.     Param($Server =$(throw ‘$Server is Required’),$App= $(throw ‘$FarmServer is Required’))
    9.     $type = [System.Type]::GetTypeFromProgID("MetaFrameCOM.MetaFrameApplication",$Server)
    10.     $mfApp = [system.Activator]::CreateInstance($type)
    11.     $mfApp.Initialize(3,"Applications\$myapp")
    12.     $mfApp.LoadData($true)
    13.     $mfApp
    14. }
    15.  
    16. ## Returns Users currently using APP
    17. ## -app: Name of Application. This is required
    18. ## -Server: Name of Farm Server. Defaults to local if not passed
    19. ## -count: Switch… if set just returns count of servers
    20. function Get-CitrixAppUsers {
    21.     Param($app = $(throw ‘$app is required’),$server,[switch]$count)
    22.     function gcf{
    23.         param($srv)
    24.         $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
    25.         $mfarm = [system.Activator]::CreateInstance($type)
    26.         $mfarm.Initialize(1)
    27.         return $mFarm
    28.     }
    29.     $ErrorActionPreference = "SilentlyContinue"
    30.     Write-host
    31.     if($server){$mfm = gcf $server}
    32.     else{$mfm = New-Object -com MetaFrameCOM.MetaFrameFarm;$mfm.Initialize(1)}
    33.     $users = $mfm.Applications | ?{($_.AppName -eq $app) -or ($_.BrowserName -eq $app)}
    34.     $Users = $users.Sessions | sort -Property UserName
    35.     if($count){
    36.         Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
    37.         Write-Host
    38.     }
    39.     else{
    40.         Write-Host ""
    41.         Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
    42.         Write-Host "—————————————————–" -ForegroundColor gray
    43.         foreach($user in $Users){
    44.             If($User.SessionState -eq 1){
    45.                 Write-Host ($User.UserName).PadRight(10) -ForegroundColor Green -NoNewline
    46.             }
    47.             else{
    48.                 Write-Host ($User.UserName).PadRight(10) -ForegroundColor yellow -NoNewline
    49.             }
    50.         }
    51.         Write-Host
    52.         Write-Host "—————————————————–" -ForegroundColor gray
    53.         Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
    54.         Write-Host
    55.     }
    56. }
    57.  
    58. ## Returns Servers currently published APP
    59. ## -app: Name of Application. This is required
    60. ## -Server: Name of Farm Server. Defaults to local if not passed
    61. ## -count: Switch… if set just returns count of servers
    62. function Get-CitrixAppServers {
    63.     Param($app = $(throw ‘$app is required’),$Server,[switch]$count)
    64.     function gcf{
    65.         param($srv)
    66.         $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
    67.         $mfarm = [system.Activator]::CreateInstance($type)
    68.         $mfarm.Initialize(1)
    69.         return $mFarm
    70.     }
    71.     if($server){$mfm = gcf $server}
    72.     else{$mfm = New-Object -com MetaFrameCOM.MetaFrameFarm;$mfm.Initialize(1)}
    73.     $Apps = $mfm.Applications | ?{($_.AppName -eq $app) -or ($_.BrowserName -eq $app)}
    74.     # for XP farms
    75.     $servers = $apps | %{$_.Servers} | sort -Property ServerName | Select-Object ServerName
    76.     if(!$servers){
    77.         # for 40/45 farms
    78.         $servers = $Apps.Sessions | Select-Object ServerName | Sort-Object -unique ServerName
    79.     }
    80.     if($count)
    81.     {
    82.         Write-Host
    83.         Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
    84.         Write-Host
    85.     }
    86.