Posts RSS Comments RSS 253 Posts and 411 Comments till now

Archive for the 'functions' Category

Format-XML (Journey to Pretty XML output)

I ran across a need to use Export-CliXML to produce human readable XML. Since, I never really cared what the output looked like so I let the blob go.

Now I care 🙂

I sent a message to the Powershell Dev Team and as always they are super speedy in response. (less than 1hr… that is INSANE.)

Enter Format-XML
http://blogs.msdn.com/powershell/archive/2008/01/18/format-xml.aspx

Help for Export-Clixml
http://technet.microsoft.com/en-us/library/bb978636.aspx

Thanks Again Jeffrey and Team!

p.s. I dont believe I ever said the output (Export-CliXML) looked like crap… just not Pretty 🙂

Using Switch -RegEx to create Custom Object (Getting HBA Info)

The other day I had a need to collect HBA (Host Base Fiber Adapters) from all my Servers. So the first place I looked was at WMI, but unfortunately… no dice. It didnt have the information I needed. The only way I knew to get the info I needed was to use was HBACmd.exe (utility to collect HBA information remotely.) So I went to writing a wrapper script in powershell to call the exe and then grep the text for what I was looking for, but I thought… HEY! Thats not the powershell thing to do! We do objects not text. So I went to parsing the text and making an object out of it. The script below is the result and while I dont believe many of you will find it particularly useful as it has a VERY specific use, I wanted to share with you how I went about objectizing the output.

Here is a Little Q and A on the script

Q: Purpose?
A: To get all the HBA information include Type, Firmware, Bios, Target Lun, WWN and a slew of other stuff.

Q: Why Did I objectize my text?
A: Because I know can simply use properties to filter and output data. Like what Machines have what Bios and what type of HBAs they have. Before I would have the parse the text for every different senario… now I just use where-object and filter away.

I few things that I wanted to point out here are the use of Switch to create the Custom Objects. IMO, Switch is one of the most powerful commands in the Powershell Language. It is INSANELY Powerfull. To be honest, It is pretty much the only one you need.

To compare it “Select Case” in vbscript would be insulting, but it can peform a similar function Like

switch ($a){
     Value1    {"It was Value 1"}
     Value2    {"It was Value 2"}
     Value3    {"It was Value 3"}
     Value4    {"It was Value 4"}
}

It would take a whole series of post to completely cover switch, but for this one I only want to go over -regex use. For complete use read the help located:
PS> Get-help About_Switch # Read it, Learn it, Love it

Some Quick Notes about Switch
– Can use RegEx, WildCard, Exact, CaseSensitive, or File options.
– It takes input via Pipeline {expression} or File. The cool thing is the pipeline can be any expression that results in piped output.
– For each match it can perform any ScritpBlock use $_ as the current Item
– It performs EVERY match on each element unless you use Continue after a match to stop processing that record

Like I said, Switch is insanely powerful. Just one of those powers is using RegEx for comparison.

Here is an example of using the -RegEx option

switch -RegEx (Get-ChildItem C:\test)
{
    "^\d"                  {"Starts with number:          " + $_.FullName}
    "\d"                   {"Has a number in it:          " + $_.FullName}
    "[^A-Za-z]"            {"Does NOT start with Number:  " + $_.FullName}
    "tmp"                  {"Has ‘tmp’ in it:             " + $_.FullName}
    # Notice that you can even use and expression to match
    {$_.Mode -match "-a"}  {"Has Archive Bit Set:         " + $_.FullName}
}

Now.. lets look at the script below. You will noticed I used RegEx to decide what value gets put in to what property of the object.

The script converts the output of three commands into two different objects. Lets look at one of them

It takes text like This

Manageable HBA List

Port WWN   : 10:00:00:00:11:11:11:11
Node WWN   : 20:00:00:00:11:11:11:11
Fabric Name: 00:00:00:00:00:00:00:00
Flags      : 0000f0a5
Host Name  : Server1
Mfg        : Emulex Corporation

Port WWN   : 10:00:00:00:22:22:22:22
Node WWN   : 20:00:00:00:22:22:22:22
Fabric Name: 00:00:00:00:00:00:00:00
Flags      : 0000f0a5
Host Name  : Server1
Mfg        : Emulex Corporation

And converts Into an object like This

  TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
—-        ———-   ———-
Equals      Method       System.Boolean Equals(Object obj)
GetHashCode Method       System.Int32 GetHashCode()
GetType     Method       System.Type GetType()
ToString    Method       System.String ToString()
Fabric      NoteProperty System.String Fabric=00:00:00:00:00:00:00:00
Flags       NoteProperty System.String Flags=0000f0a5
HBADetail   NoteProperty System.Object[] HBADetail=System.Object[]
Host        NoteProperty System.String Host=Server1
LUN         NoteProperty System.String LUN=01
MFG         NoteProperty System.String MFG=Emulex Corporation
NodeWWN     NoteProperty System.String NodeWWN=20:00:00:00:11:11:11:11
PortWWN     NoteProperty System.String PortWWN=10:00:00:00:11:11:11:11

Here is the Script

Param($List,$HostName,[switch]$FullDetail,[switch]$Verbose)
Begin{
    $erroractionpreference = "SilentlyContinue"
    $HBACMDPath = "<path To HbaCmd.exe>"
    function CreateHBAListObj{
        Param($srv)
        $objCol = @()
        $result = &"$HBACMDPath" "h=$srv" ListHBAs
        foreach($item in $result)
        {
            $parsd = $item.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)
            switch -regex ($parsd[0])
            {
                "^Port" {
                            $myobj = "" | Select-Object Host,PortWWN,NodeWWN,Fabric,Flags,LUN,MFG
                            $myobj.PortWWN = $parsd[1]
                            $myobj.Lun = GetTargetLun $srv $myobj.PortWWN
                        }
                "^Node" {$myobj.NodeWWN = $parsd[1]}
                "^Fabr" {$myobj.Fabric  = $parsd[1]}
                "^Flag" {$myobj.Flags   = $parsd[1]}
                "^Host" {$myobj.Host    = $parsd[1]}
                "^MFG " {
                            $myobj.MFG     = $parsd[1]
                            $objCol += $myObj
                        }
            }
        }
        $objCol
    }
    function CreateHBAInfoObj{
        Param($srv,$wwn)
        $objCol = @()
        $result = &"$HBACMDPath" "h=$srv" HBAAttrib $wwn

        $myobj = "" |Select-Object Host,MFG,SN,Model,ModelDesc,NodeWWN,NodeSymname,
                                   HWVersion,ROM,FW,VenderID,Ports,DriverName,DeviceID,HBAType,
                                   OpFW,SLT1FW,SLT2FW,IEEEAddress,BootBios,DriverVer,KernelVer
        foreach($item in $result)
        {
            $parsd = $item.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)
            switch -regex ($parsd[0])
            {
                "^Host"         {$myobj.Host         = $parsd[1]}
                "^Manufacturer" {$myobj.MFG          = $parsd[1]}
                "^Serial"       {$myobj.Sn           = $parsd[1]}
                "^Model  "      {$myobj.Model        = $parsd[1]}
                "^Model Desc"   {$myobj.ModelDesc    = $parsd[1]}
                "^Node WWN "    {$myobj.NodeWWN      = $parsd[1]}
                "^Node Symname" {$myobj.NodeSymname  = $parsd[1]}
                "^HW"           {$myobj.HWVersion    = $parsd[1]}
                "^Opt"          {$myobj.ROM          = $parsd[1]}
                "^FW"           {$myobj.FW           = $parsd[1]}
                "^Vender"       {$myobj.VenderID     = $parsd[1]}
                "^Number"       {$myobj.Ports        = $parsd[1]}
                "^Driver Name"  {$myobj.DriverName   = $parsd[1]}
                "^Device"       {$myobj.DeviceID     = $parsd[1]}
                "^HBA Type"     {$myobj.HBAType      = $parsd[1]}
                "^Operational"  {$myobj.OpFW         = $parsd[1]}
                "^SLI1 FW"      {$myobj.SLT1FW       = $parsd[1]}
                "^SLI2 FW"      {$myobj.SLT2FW       = $parsd[1]}
                "^IEEE"         {$myobj.IEEEAddress  = $parsd[1]}
                "^Boot "        {$myobj.BootBios     = $parsd[1]}
                "^Driver Ver"   {$myobj.DriverVer    = $parsd[1]}
                "^Kernel "      {$myobj.KernelVer    = $parsd[1]
                                 $objCol += $myObj}
            }
        }
        $objCol
    }
    function GetTargetLun{
        Param($srv,$wwn)
        $objCol = @()
        $result = &"$HBACMDPath" "h=$srv" TargetMapping $wwn
        [int]$lun = 0
        switch -regex ($result)
        {
            "^SCSI OS Lun" {$lun = $_.split([string[]](": "),[system.StringSplitOptions]::RemoveEmptyEntries)[1].trim()}
        }
        "{0:x}" -f $lun
    }
    function Ping-Server {
        Param([string]$srv)
        if($srv -eq ""){return $false}
        $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
        if($pingresult.statuscode -eq 0) {$true} else {$false}
    }
    Write-Host
    if($verbose){$VerbosePreference = "Continue"}
}
Process{
    if($_)
    {
        Write-Host "Getting HBA Info from $_"
        if($FullDetail)
        {
            $MyObject = CreateHBAListObj $_
            $HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
            $MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
            $MyObject
        }
        else
        {
            $MyObject = CreateHBAListObj $_
            $MyObject
        }
    }
}
End{
    if($list)
    {
        $servers = Get-Content $list
        Write-Host "Running HBA Check against Servers in $list"
        foreach($server in $servers)
        {
            if($server -ne "")
            {
                if(ping-server $server)
                {
                    Write-Host "Getting HBA Info from $server"
                    if($FullDetail)
                    {
                        $MyObject = CreateHBAListObj $server
                        $HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
                        $MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
                        $MyObject
                    }
                    else
                    {
                        $MyObject = CreateHBAListObj $server
                        $MyObject
                    }
                }
                else
                {
                    Write-Host "$Server not Pingable `n" -foregroundcolor RED
                }
            }
        }
    }
    if($HostName)
    {
        Write-Host "Running HBA Check against Servers in $HostName"
        if(ping-server $HostName)
        {
            Write-Host "Getting HBA Info from $HostName"
            if($FullDetail)
            {
                $MyObject = CreateHBAListObj $HostName
                $HBADetail = $MyObject | %{CreateHBAInfoObj $_.Host $_.PortWWN}
                $MyObject | add-Member -Name HBADetail -type NoteProperty -Value $HBADetail -force
                $MyObject
            }
            else
            {
                $MyObject = CreateHBAListObj $HostName
                $MyObject
            }
        }
        else
        {
            Write-Host "$HostName not Pingable `n" -foregroundcolor RED
        }
    }
    Write-Host
}

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.

Param($Server,$log,[switch]$Farm,[switch]$Verbose,[switch]$debug)
# Get-CitrixHotfix.ps1

if($Verbose){$VerbosePreference = "Continue"}
if($debug){Set-PSDebug -Step}

Write-Host "`nProcessing…`n"

if($Farm)
{
    # Get Farm Object
    $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
    $CTXFarm = [system.Activator]::CreateInstance($type)
    $CTXFarm.Initialize(1)

    # Creating Collection for Custom Objects
    $myCol = @()

    foreach($Srv in $CTXFarm.Servers)
    {
        # Create Custom Object
        $myobj = "" | Select-Object Name,Hotfix
        $myobj.Name = $Srv.ServerName
        $myobj.Hotfix = @()

        Write-Verbose $Srv.ServerName

        # Get Hotfix Information for the Server and add to Custom Object
        $CTXServer = $CTXFarm.GetServer2(6,$Srv.ServerName)
        $CTXServer.winServerObject2.hotfixes | %{Write-Verbose " – $($_.Name)";$myobj.HotFix += $_.name}

        # Add Server Object to Collection
        $myCol += $myobj
    }
    # Output Collection
    if($log)
    {
        @(foreach($obj in $mycol)
        {
            Write-Output $obj.Name
            foreach($hf in $obj.Hotfix){write-Output " – $hf"}
        }) | out-File $log -enc ASCII
    }
    else
    {
        $mycol
    }
}
else
{
    $myobj = "" | Select-Object Name,HotFix
    $myobj.Name = $Server
    $myobj.HotFix = @()
    $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
    $mfServer = [system.Activator]::CreateInstance($type)
    $mfServer.Initialize(6,$Server)
    Write-Output $Server
    $mfServer.winServerObject2.hotfixes | foreach-Object{$myobj.HotFix += $_.Name}
    $myobj
}

write-host

if($debug){Set-PSDebug -off}

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

    ##########################################
    ####     Citrix Server Functions      ####
    ##########################################

    ## Publish Application to Server(s)
    ## -app: Name of Application to remove. This is required
    ## -Server: Name of Server
    ## PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    ## NOTE: App Name must include subfolders of the app.
    ##       If the app in in Applications\Test then app would be Test\MyApp
    ##       Example: Publish-CitrixApplication -server myserver -app Test\MyApp
    function Publish-CitrixApplication{
        Param([string]$server,[string]$app)
        Begin{
            Write-Host
            function gcs{
                Param($srv)
                $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
                $mfServer = [system.Activator]::CreateInstance($type)
                $mfServer.Initialize(6,$Server)
                $mfServer
            }
            function gca{
                Param($srv,$ma)
                $type = [System.Type]::GetTypeFromProgID("MetaFrameCOM.MetaFrameApplication",$Server)
                $mfApp = [system.Activator]::CreateInstance($type)
                $mfApp.Initialize(3,"Applications\$ma")
                $mfApp.LoadData($true)
                $mfApp
            }
            function cPublish {
                Param([string]$Srv,[string]$myapp)
                $Srv = $Srv.toUpper()
                $mfSrv = gcs $srv
                $mfApp = gca $srv $myapp
                $mfAppBinding = New-Object -ComObject MetaFrameCOM.MetaFrameAppSrvBinding
                $mfAppBinding.Initialize(6,$Srv,"Applications\$app")
                if($mfAppBinding)
                {
                    Write-Host "Publishing App[$myapp] on Server [$Srv]" -ForegroundColor Green
                    $mfApp.AddServer($mfAppBinding)
                    $mfApp.SaveData()
                }
                else
                {
                    Write-Host "Unable To Create App Binding" -ForegroundColor Red
                }
            }
            $process = @()
        }
        Process{
            if($_){
                if($_.ServerName){
                    $process += $_.ServerName
                }
                else{
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            foreach($s in $process){
                cPublish -srv $s -myapp $app
                Write-Host
            }
        }
    }

    ## UnPublishes ALL Application from Server(s)
    ## -Server: Name of Server
    ## PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    function UnPublish-CitrixServer{
        Param([string]$server)
        Begin{
            function gcs{
                Param($srv)
                $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
                $mfServer = [system.Activator]::CreateInstance($type)
                $mfServer.Initialize(6,$Server)
                $mfServer
            }
            function cUnPublish {
                Param([string]$Srv)
                $Srv = $Srv.toUpper()
                $mfSrv = gcs $srv
                If($mfSrv.Applications.Count -gt 0)
                {
                    Write-Host "Removing All Published Applications from $Srv" -ForegroundColor Red
                    Write-Host "===================================================" -ForegroundColor Green
                    ForEach($a in $mfSrv.Applications)
                    {
                        $myApp = $a.AppName
                        Write-Host "Removing App [$myApp] from Server [$Srv]" -ForegroundColor White
                        $a.RemoveServer($Srv)
                        $a.SaveData()
                    }
                }
                else
                {
                    Write-Host "No Published Applications for $Srv" -ForegroundColor Red
                }
            }
            Write-Host
            $process = @()
        }
        Process{
            if($_){
                if($_.ServerName)
                {
                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            foreach($s in $process){
                cUnPublish $s
                Write-Host
            }
        }
    }

    ## Remove Specific Application from Server(s)
    ## -app: Name of Application to remove. This is required
    ## -Server: Name Server.
    ## PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    function Remove-CitrixApplication {
        Param([string]$server,[string]$app)
        Begin{
            function gcs{
                Param($srv)
                $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
                $mfServer = [system.Activator]::CreateInstance($type)
                $mfServer.Initialize(6,$Server)
                $mfServer
            }
            function RemoveApp {
                Param([string]$Srv,[string]$myapp)
                $AppRemoved = $false
                $Srv = $Srv.toUpper()
                $mfSrv = gcs $srv
                If($mfSrv.Applications.Count -gt 0)
                {
                    ForEach($a in $mfSrv.Applications)
                    {
                        If(($a.AppName -eq "$myapp") -or ($a.BrowserName -eq "$myapp"))
                        {
                            Write-Host "Removing App [$myApp] from Server [$Srv]" -ForegroundColor Green
                            $a.RemoveServer($Srv)
                            $a.SaveData()
                            $AppRemoved = $true
                        }
                    }
                }
                else
                {
                    Write-Host "No Applications Published for $Srv" -ForegroundColor Red
                    $AppRemoved = $true
                }
                If($AppRemoved -eq $false)
                {
                    Write-Host "This Application not Published for $Srv" -ForegroundColor Red
                }
            }
            Write-Host
            $process = @()
        }
        Process{
            if($_)
            {
                if($_.ServerName){

                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
            foreach($s in $process)
            {
                RemoveApp -Srv $s -myapp $app
                Write-Host
            }
        }
    }

    Citrix App Functions

    #########################################
    ####     Citrix App Functions        ####
    #########################################
    ## Returns Citrix Application for Farm
    ## -Server: Name of Farm Server. This is required
    ## -App: Name of Application to remove. This is required
    function Get-CitrixApp{
        Param($Server =$(throw ‘$Server is Required’),$App= $(throw ‘$FarmServer is Required’))
        $type = [System.Type]::GetTypeFromProgID("MetaFrameCOM.MetaFrameApplication",$Server)
        $mfApp = [system.Activator]::CreateInstance($type)
        $mfApp.Initialize(3,"Applications\$myapp")
        $mfApp.LoadData($true)
        $mfApp
    }

    ## Returns Users currently using APP
    ## -app: Name of Application. This is required
    ## -Server: Name of Farm Server. Defaults to local if not passed
    ## -count: Switch… if set just returns count of servers
    function Get-CitrixAppUsers {
        Param($app = $(throw ‘$app is required’),$server,[switch]$count)
        function gcf{
            param($srv)
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
            $mfarm = [system.Activator]::CreateInstance($type)
            $mfarm.Initialize(1)
            return $mFarm
        }
        $ErrorActionPreference = "SilentlyContinue"
        Write-host
        if($server){$mfm = gcf $server}
        else{$mfm = New-Object -com MetaFrameCOM.MetaFrameFarm;$mfm.Initialize(1)}
        $users = $mfm.Applications | ?{($_.AppName -eq $app) -or ($_.BrowserName -eq $app)}
        $Users = $users.Sessions | sort -Property UserName
        if($count){
            Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
            Write-Host
        }
        else{
            Write-Host ""
            Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
            Write-Host "—————————————————–" -ForegroundColor gray
            foreach($user in $Users){
                If($User.SessionState -eq 1){
                    Write-Host ($User.UserName).PadRight(10) -ForegroundColor Green -NoNewline
                }
                else{
                    Write-Host ($User.UserName).PadRight(10) -ForegroundColor yellow -NoNewline
                }
            }
            Write-Host
            Write-Host "—————————————————–" -ForegroundColor gray
            Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
            Write-Host
        }
    }

    ## Returns Servers currently published APP
    ## -app: Name of Application. This is required
    ## -Server: Name of Farm Server. Defaults to local if not passed
    ## -count: Switch… if set just returns count of servers
    function Get-CitrixAppServers {
        Param($app = $(throw ‘$app is required’),$Server,[switch]$count)
        function gcf{
            param($srv)
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
            $mfarm = [system.Activator]::CreateInstance($type)
            $mfarm.Initialize(1)
            return $mFarm
        }
        if($server){$mfm = gcf $server}
        else{$mfm = New-Object -com MetaFrameCOM.MetaFrameFarm;$mfm.Initialize(1)}
        $Apps = $mfm.Applications | ?{($_.AppName -eq $app) -or ($_.BrowserName -eq $app)}
        # for XP farms
        $servers = $apps | %{$_.Servers} | sort -Property ServerName | Select-Object ServerName
        if(!$servers){
            # for 40/45 farms
            $servers = $Apps.Sessions | Select-Object ServerName | Sort-Object -unique ServerName
        }
        if($count)
        {
            Write-Host
            Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
            Write-Host
        }
        else
        {
            Write-Host ""
            Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
            Write-Host "———————————————–" -ForegroundColor gray
            foreach($server in $servers){Write-Host "$($server.ServerName)" -ForegroundColor Green}
            Write-Host "———————————————–" -ForegroundColor gray
            Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
            Write-Host ""
        }
    }

    ## Returns Server(s) user is logged into via Citrix
    ## -LoginName: Login Name of user (Domain\User). This is Required
    ## -Server: Name of Farm Server. Defaults to local if not passed
    ## -Verbose: Details about User
    function Find-CitrixUser {
        Param([string]$LoginName,$Server,[switch]$verbose)
        $user = $LoginName.Split("\")[1]
        $Domain = $LoginName.Split("\")[0]
        if($server)
        {
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeUser",$Server)
            $mfuser = [system.Activator]::CreateInstance($type)
            $mfuser.Initialize(1,$Domain,1,$user)
        }
        else
        {
            $mfuser = New-Object -ComObject MetaframeCOM.MetaframeUser
            $mfuser.Initialize(1,$Domain,1,$user)
        }
        Write-Host
        Write-Host "User: $($mfuser.UserName) found on the Following:"
        foreach ($s in $mfuser.Sessions)
        {
            if($verbose)
            {
                Write-Host
                Write-Host "$($s.ServerName)"
                Write-Host "-=-=-=-=-=-"
                Write-Host "AppName          : $($s.AppName)" -foregroundcolor yellow
                Write-Host "SessionName      : $($s.SessionName)" -foregroundcolor yellow
                Write-Host "SessionID        : $($s.SessionID)" -foregroundcolor yellow
                Write-Host "ClientAddress    : $($s.ClientAddress)" -foregroundcolor yellow
                Write-Host "ClientEncryption : $($s.ClientEncryption)" -foregroundcolor yellow
                Write-Host
                Write-Host "Processes"
                Write-Host "========="
                foreach ($proc in $s.Processes)
                {
                    Write-Host $proc.ProcessName -foregroundcolor Green
                }
                Write-host
            }
            else
            {
                write-Host "   -> $($s.ServerName)"
            }
        }
    }

    Citrix, Citrix, and more Citrix!

    It has been a while since I dove into the MFCom pool. Most of the Citrix functions I wrote where at the very beggining of my Powershell Adventure. I decided to go through and rewrite some of them and post the others.

    So… here you go.

    I broke these down into three sections
    – Citrix Farm Functions
    – Citrix App Functions
    – Citrix Server Functions

    If you get a chance to use these let me know if you run into any troubles… I did test them thoroughly.. but you never know.

    Other than Publish/Unpublish/Remove they shouldn’t change anything.

    UPDATED: I added information about functions before each section. And a list at the bottom!

    Farm functions
    ————–
    # Returns Farm Object.
    # -Server: Any PS should do.. I recommend ZDC though) !!REQ!!
    Get-CitrixFarm

    # Returns a collection of Server Objects
    # -zone= This will be the zone name (normally the subnet i.e. 10.1.1.0) !!REQ!!
    Get-CitrixOnline

    # Returns Load Evaluators Information
    # -Server: Any PS should do.. I recommend ZDC though) !!REQ!!
    Get-CitrixLE

    # Returns Printer Drivers install in Farm
    # -Server: Any PS should do.. I recommend ZDC though) !!REQ!!
    # This can take a REAL long time
    Get-CitrixPrintDrivers

    # Returns Polices in the Farm
    # -Server !!REQ!! (any PS should do.. I recommend ZDC though)
    Get-CitrixPolicies

    #########################################
    ####     Citrix Farm Functions       ####
    #########################################
    # Get Citrix Farm
    function Get-CitrixFarm{
        param($Server)
        $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$Server)
        $mfarm = [system.Activator]::CreateInstance($type)
        $mfarm.Initialize(1)
        return $mFarm
    }

    # Get Online Servers by Zone
    function Get-CitrixOnline {
        Param($zone)
        $mfzone = New-Object -ComObject MetaFrameCOM.MetaFrameZone
        $mfzone.Initialize($zone)
        $servers = $mfzone.OnlineServers
        $servers
    }

    # Get Citrix Load Evaluators (only 4.0/4.5)
    function Get-CitrixLE{
        Param($server=$(throw "Server is Required"))
        function Load-Farm{
            param($srv)
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$srv)
            $mfarm = [system.Activator]::CreateInstance($type)
            $mfarm.Initialize(1)
            return $mFarm
        }
        $Farm = load-farm $server
        if($Farm.LoadEvaluators){
            foreach($eval in $Farm.LoadEvaluators)
            {
                $eval.loadData(1)
                "+ Load Evaluator: {0}" -f $eval.LEName
                $servers = $eval.AttachedServers(1)
                if($servers.count -ne 0)
                {
                    "  + Servers"
                    $servers | %{"    – {0}" -f $_.ServerName}
                }
                $rules = $eval.rules | Select-Object RuleType,HWM,LWM,Schedules
                if($rules.count -ne 0)
                {
                    "  + Rules"
                    foreach($rule in $rules)
                    {
                        "    – {0}" -f $rule
                    }
                }
            }
        }
    }

    # Gets the Citrix Printer Drivers for the Farm (Can be REAL slow)
    function Get-CitrixPrintDrivers{
        Param($server=$(throw "Server is Required"))
        function Load-Farm{
            param($srv)
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$srv)
            $mfarm = [system.Activator]::CreateInstance($type)
            $mfarm.Initialize(1)
            return $mFarm
        }
        $farm = Load-Farm $Server
        $farm.Drivers
    }

    # Gets Citrix Policies
    function Get-CitrixPolicies{
        param($Server)
        function Load-Farm{
            param($srv)
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$srv)
            $mfarm = [system.Activator]::CreateInstance($type)
            $mfarm.Initialize(1)
            return $mFarm
        }
        $farm = Load-Farm $server
        $type = [System.Type]::GetTypeFromProgID("MetaFrameCOM.MetaFrameUserPolicy")
        foreach($pol in $Farm.policies($type))
        {
            $pol.loadData(1)
            "+ Name: {0}" -f $pol.Name
            "  – Description: {0}" -f $pol.Description
            "  – Enabled: {0}" -f $pol.Enabled
            if($pol.AllowedAccounts)
            {
                "  + AllowedAccounts"
                foreach($aa in $pol.AllowedAccounts)
                {
                    "    – {0}" -f $aa.AccountName
                }
            }
            if($pol.UserPolicy2)
            {
                "  + UserPolicy"
                $props = $pol.UserPolicy2 | Get-Member -membertype Property | %{$_.Name} | Sort-Object Name
                foreach($prop in $props)
                {
                    if(($pol.UserPolicy2.$prop -match "\d") -and ($pol.UserPolicy2.$prop -ne 0))
                    {
                        "     – {0}:{1}" -f $prop,$pol.UserPolicy2.$prop
                    }
                }
            }
            write-Output " "
        }
    }

    App functions
    ————–
    # Returns User Count for an App or All Apps
    # -FarmServer !!REQ!! (any PS should do.. I recommend ZDC though)
    # -app: Name of Application
    # This loops CTRL+C to break
    Get-ApplicationUserCount

    # Returns Server(s) user is logged into via Citrix
    # -LoginName: Login Name of user (Domain\User) !!REQ!!
    # -Verbose: Details about User
    Find-CitrixUser

    # Returns Servers published for Application or just Count
    # -app: Name of Application !!REQ!!
    # -count: Switch… if set just returns count of servers
    Get-CitrixServers

    # Returns Users currently using APP
    # -app: !!REQ!!
    # -count: Switch… if set just returns count of servers
    Get-CitrixAppUsers

    #########################################
    ####     Citrix App Functions        ####
    #########################################
    # Outputs the number of Users using a Citrix App or Apps
    function Get-ApplicationUserCount {
        Param([string]$app,[string]$farmServer = $(throw ‘$FarmServer is Required’))
        function List-AllCitrixApps{
            Param($mFarm)
            ForEach($app in $mFarm.Applications)
            {
                $name = $app.BrowserName.PadRight(25)
                $count = "$($app.Sessions.Count)"
                $count = $count.PadRight(10)
                Write-Host "$name $count"
            }
        }
        function List-App{
            param($mApp,$mfFarm)
            ForEach($app in $mfFarm.Applications)
            {
                if($app.BrowserName -eq "$mApp")
                {
                    $name = $app.BrowserName.PadRight(25)
                    $count = "$(($app.Sessions | ?{$_.SessionState -eq 1}).Count)"
                    $count = $count.PadRight(10)
                    Write-Host "$name $count"
                }
            }
        }
        function Load-Farm{
            $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeFarm",$srv)
            $mfarm = [system.Activator]::CreateInstance($type)
            $mfarm.Initialize(1)
            return $mFarm
        }
        Write-Host
        $title1 = "Application".PadRight(25)
        $title2 = "===========".PadRight(25)
        Write-Host "$title1 User Count" -ForegroundColor White
        Write-Host "$title2 ==========" -ForegroundColor Red
        $mf = Load-Farm $farmServer
        While($true)
        {
            $oldpos = $host.UI.RawUI.CursorPosition
            If($app)
            {
                List-App $app $mf
            }
            else
            {
                List-AllCitrixApps $mf
            }
            sleep(5)
            $host.UI.RawUI.CursorPosition = $oldpos
        }
        Write-Host ""
    }

    # Finds what Server a User is on
    function Find-CitrixUser {
        Param([string]$LoginName,[switch]$verbose)
        $user = $LoginName.Split("\")[1]
        $Domain = $LoginName.Split("\")[0]
        $mfuser = New-Object -ComObject MetaframeCOM.MetaframeUser
        $mfuser.Initialize(1,$Domain,1,$user)
        Write-Host
        Write-Host "User: $($mfuser.UserName) found on the Following:"
        foreach ($s in $mfuser.Sessions)
        {
            if($verbose)
            {
                Write-Host
                Write-Host "$($s.ServerName)"
                Write-Host "-=-=-=-=-=-"
                Write-Host "AppName          : $($s.AppName)" -foregroundcolor yellow
                Write-Host "SessionName      : $($s.SessionName)" -foregroundcolor yellow
                Write-Host "SessionID        : $($s.SessionID)" -foregroundcolor yellow
                Write-Host "ClientAddress    : $($s.ClientAddress)" -foregroundcolor yellow
                Write-Host "ClientEncryption : $($s.ClientEncryption)" -foregroundcolor yellow
                Write-Host
                Write-Host "Processes"
                Write-Host "========="
                foreach ($proc in $s.Processes)
                {
                    Write-Host $proc.ProcessName -foregroundcolor Green
                }
                Write-host
            }
            else
            {
                write-Host "   -> $($s.ServerName)"
            }
        }
    }

    # Gets Servers Published for specified App (or just returns count)
    function Get-CitrixServers {
        Param($app = $(throw ‘$app is required’),[switch]$count)
        $mfm = New-Object -com MetaFrameCOM.MetaFrameFarm
        $mfm.Initialize(1)
        $servers = $mfm.Applications | ?{$_.AppName -eq $app}
        $servers = $servers.Servers | sort -Property ServerName
        if($count)
        {
            Write-Host
            Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
            Write-Host
        }
        else
        {
            Write-Host ""
            Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
            Write-Host "———————————————–" -ForegroundColor gray
            foreach($server in $servers){Write-Host "$($server.ServerName)" -ForegroundColor Green}
            Write-Host "———————————————–" -ForegroundColor gray
            Write-Host "Found [$($Servers.Count)] Servers for Application [$app]" -ForegroundColor White
            Write-Host ""
        }
    }

    # Returns Users currently using Citrix App
    function Get-CitrixAppUsers {
        Param($app = $(throw ‘$app is required’),[switch]$count)
        $ErrorActionPreference = "SilentlyContinue"
        Write-host
        $mfm = New-Object -com MetaFrameCOM.MetaFrameFarm
        $mfm.Initialize(1)
        $users = $mfm.Applications | ?{$_.AppName -eq $app}
        $Users = $users.Sessions | sort -Property UserName
        if($count){
            Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
            Write-Host
        }
        else{
            Write-Host ""
            Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
            Write-Host "—————————————————–" -ForegroundColor gray
            foreach($user in $Users){
                If($User.SessionState -eq 1){
                    Write-Host ($User.UserName).PadRight(10) -ForegroundColor Green -NoNewline
                }
                else{
                    Write-Host ($User.UserName).PadRight(10) -ForegroundColor yellow -NoNewline
                }
            }
            Write-Host
            Write-Host "—————————————————–" -ForegroundColor gray
            Write-Host "Found [$($Users.Count)] Users for Application [$app]" -ForegroundColor White
            Write-Host
        }
    }

    Server functions
    ————–
    # Returns A Server Object
    # -Server: Name of the Server !!REQ!!
    Get-CitrixServer

    # Publishes Application to Server(s)
    # -app: !!REQ!!
    # -Server: Name of Server
    # PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    Publish-CitrixApplication

    # UnPublishes ALL Application from Server(s)
    # -Server: Name of Server
    # PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    UnPublish-CitrixServer

    # Remove Specific Application from Server(s)
    # -app: !!REQ!!
    # -Server: Name of Server
    # PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    Remove-CitrixApplication

    # Gets Published Applications from Server(s)
    # -Server: Name of Server
    # PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    Get-CitrixApplications

    # Returns Count of Logged on Users from Server(s)
    # -Server: Name of Server
    # PIPED: It will take Servers via Pipe. It expects a list or Citrix Server Object
    Get-TSUserCount

    ##########################################
    ####     Citrix Server Functions      ####
    ##########################################
    # Get a Citrix Server Object
    function Get-CitrixServer{
        Param($Server)
        $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$Server)
        $mfServer = [system.Activator]::CreateInstance($type)
        $mfServer.Initialize(6,$Server)
        $mfServer
    }

    # Publish Application to Server(s)
    function Publish-CitrixApplication{
        Param([string]$server,[string]$app)
        Begin{
            Write-Host
            function cPublish {
                Param([string]$Srv,[string]$myapp)
                $Srv = $Srv.toUpper()
                $mfSrv = New-Object -ComObject MetaFrameCOM.MetaFrameServer
                $mfSrv.Initialize(6,"$Srv")
                $mfApp = New-Object -ComObject MetaFrameCOM.MetaFrameApplication
                $mfApp.Initialize(3,"Applications\$myapp")
                $mfApp.LoadData($true)
                $mfAppBinding = New-Object -ComObject MetaFrameCOM.MetaFrameAppSrvBinding
                $mfAppBinding.Initialize(6,$Srv,"Applications\$app")
                if($mfAppBinding)
                {
                    Write-Host "Publishing App[$myapp] on Server [$Srv]" -ForegroundColor Green
                    $mfApp.AddServer($mfAppBinding)
                    $mfApp.SaveData()
                }
                else
                {
                    Write-Host "Unable To Create App Binding" -ForegroundColor Red
                }
            }
            $process = @()
        }
        Process{
            if($_){
                if($_.ServerName){
                    $process += $_.ServerName
                }
                else{
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            foreach($s in $process){
                cPublish -srv $s -myapp $app
                Write-Host
            }
        }
    }

    # UnPublish All Application from Server(s)
    function UnPublish-CitrixServer{
        Param([string]$server)
        Begin{
            function cUnPublish {
                Param([string]$Srv)
                $Srv = $Srv.toUpper()
                $mfSrv = New-Object -ComObject MetaFrameCOM.MetaFrameServer
                $mfSrv.Initialize(6,"$Srv")
                If($mfSrv.Applications.Count -gt 0)
                {
                    Write-Host "Removing All Published Applications from $Srv" -ForegroundColor Red
                    Write-Host "===================================================" -ForegroundColor Green
                    ForEach($a in $mfSrv.Applications)
                    {
                        $myApp = $a.AppName
                        $a.LoadData(1)
                        Write-Host "Removing App [$myApp] from Server [$Srv]" -ForegroundColor White
                        $a.RemoveServer($Srv)
                        $a.SaveData()
                    }
                }
                else
                {
                    Write-Host "No Published Applications for $Srv" -ForegroundColor Red
                }
            }
            Write-Host
            $process = @()
        }
        Process{
            if($_){
                if($_.ServerName)
                {
                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            foreach($s in $process){
                cUnPublish $s
                Write-Host
            }
        }
    }

    # Remove a Citrix App from Server
    function Remove-CitrixApplication {
        Param([string]$server,[string]$app)
        Begin{
            function RemoveApp {
                Param([string]$Srv,[string]$myapp)
                $AppRemoved = $false
                $Srv = $Srv.toUpper()
                $mfSrv = New-Object -ComObject MetaFrameCOM.MetaFrameServer
                $mfSrv.Initialize(6,"$Srv")
                If($mfSrv.Applications.Count -gt 0)
                {
                    ForEach($a in $mfSrv.Applications)
                    {
                        If($a.AppName -eq "$myapp")
                        {
                            Write-Host "Removing App [$myApp] from Server [$Srv]" -ForegroundColor Green
                            $a.RemoveServer($Srv)
                            $a.SaveData()
                            $AppRemoved = $true
                        }
                    }
                }
                else
                {
                    Write-Host "No Applications Published for $Srv" -ForegroundColor Red
                    $AppRemoved = $true
                }
                If($AppRemoved -eq $false)
                {
                    Write-Host "This Application not Published for $Srv" -ForegroundColor Red
                }
            }
            Write-Host
            $process = @()
        }
        Process{
            if($_)
            {
                if($_.ServerName){

                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
            foreach($s in $process)
            {
                RemoveApp -Srv $s -myapp $app
                Write-Host
            }
        }
    }

    # List Citrix Apps Published to Server
    function Get-CitrixApplications {
        Param([string]$Server)
        Begin {
            Write-Host
            function cGetApps {
                param([string]$srv)
                $srv = $srv.ToUpper()
                $mfsrv = New-Object -ComObject MetaFrameCOM.MetaFrameServer
                $mfsrv.Initialize(6,"$srv")
                Write-Host "SERVER $srv" -foregroundcolor Red
                Write-Host "==================" -ForegroundColor Green
                If($mfSrv.Applications.Count -gt 0) {
                    $mfSrv.Applications | %{Write-Host "Published:   $($_.AppName.ToUpper())"}
                }
                else {
                    Write-Host "No Applications Published for $srv" -foregroundcolor white
                }
            }
            $process = @()
        }
        Process{
            if($_){
                if($_.ServerName){
                    $process += $_.ServerName
                }
                else{
                    $process += $_
                }
            }
        }
        End {
            if($Server){$Process += $Server}
            foreach($s in $process){
                cGetApps $s
                Write-Host
            }
        }
    }

    # Return Current Terminal Server User Count
    function Get-TSUserCount {
        Param([string]$Server)
        Begin{
            function TsUserCount {
                param([string]$srv)
                $msg = "Checking For Users on Server [$srv]"
                $msg = $msg.PadRight($pad)
                Write-host $msg -ForegroundColor White
                $msg = "==========================================="
                $msg = $msg.PadRight($pad)
                Write-host $msg -ForegroundColor gray
                $msg = "Terminal Server User Count on Server "
                $msg1 = "[$srv]"
                $msg1 = $msg1.PadRight($pad)
                $ts = Get-WmiObject Win32_PerfFormattedData_TermService_TerminalServices -ComputerName $srv
                $count = $ts.activeSessions
                If($count -eq 0)
                {
                    Write-host "$msg [Users:$count]" -ForegroundColor Green
                }
                else
                {
                    Write-host "$msg [Users:$count]" -ForegroundColor Yellow
                }
            }
            $process = @()
        }
        Process{
            if($_){
                if($_.ServerName)
                {
                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
            foreach($s in $process)
            {
                TSUserCount $s
                Write-Host
            }
        }
    }

    Farm functions
    ————–
    Get-CitrixFarm
    Get-CitrixOnline
    Get-CitrixLE
    Get-CitrixPrintDrivers
    Get-CitrixPolicies

    App functions
    ————–
    Get-ApplicationUserCount
    Find-CitrixUser
    Get-CitrixServers
    Get-CitrixAppUsers

    Server functions
    ————–
    Get-CitrixServer
    Publish-CitrixApplication
    UnPublish-CitrixServer
    Remove-CitrixApplication
    Get-CitrixApplications
    Get-TSUserCount

    Run-Command.ps1 : Run External Commands with Power!

    I was working late tonight and we had to run a bunch of third party EXEs and such. We do this a good bit so I can’t always avoid calling external executables and I also find psexec.exe much easier than any powershell way to run remote commands. That said I find myself constantly writing this.

    $servers = Get-Content $file
    foreach($server in $servers)
    {
       Do Something Here Like
       psexec \\$server mycmd.exe param1
    }

    I decided to write a script call Run-Command.ps1.
    This will take three parameters
    – File (list of servers to process)
    – Cmd (Command to run with %S% where you want the server name to be replaced)
    – Check (just shows what command would run)
    – Will also take Piped Input

    Example:
    PS> .\Run-Command.ps1 -file c:\serverlist.txt -cmd “psexec \\%S% mycmd.exe Hello World” -check

    Run-Command.ps1

    Param($file,$cmd,[switch]$whatif,[switch]$verbose)
    Begin{
        function Ping-Server {
            Param([string]$srv)
            $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
            if($pingresult.statuscode -eq 0) {$true} else {$false}
        }
        $servers = @()
    }
    Process{
        if($_)
        {
            if($_.ServerName){$servers += $_.ServerName}
            else{$servers += $_}
        }
    }
    End{
        if($file){Get-Content $file | %{$servers += $_}}
        foreach($server in $servers)
        {
            if(ping-server $server)
            {
                if($verbose){Write-Host "+ Processing Server $Server"}
                $mycmd = $cmd -replace "\%S\%",$Server
                if($whatif){Write-Host "  – WOULD RUN $mycmd"}
                else{if($verbose){Write-Host "  – Running $mycmd"};invoke-Expression $mycmd}
            }
            else
            {
                Write-Host "+ $Server FAILED PING" -foregroundcolor RED
            }
        }
    }

    Backup-EventLog

    ## UPDATED… ADDED a EVT format Script as Well ##

    I saw a post on EE that backups up the eventlogs on Server using VBScript… I wanted to see what I could do with Powershell and this is what I came up with. I put this together pretty quick, so not much Error checking or anything, but the vbscript was 207 lines long without comments.

    Basically it does the following
    – Takes a BackupLoccation as a Parameter
    – Takes a List file or a -FromAD switch
    – List gets the computers from a file
    – FromAD gets computers from AD
    – Creates a Backup folder Named -Logs-
    – Like Server1-Logs-09110750
    – Processes Each Event Log and backs up to a File
    – Clears Log
    – I put the output of the script on the bottom.
    – NOTE: Security Logs Take awhile. I assume this because I am Generating Events by Reading the Log.

    Param($BackupLocation,$list,$FromAD)
    function Get-ADComputers{
        $filter = "(&(objectcategory=computer))"
        $root = [ADSI]""
        $props = "dNSHostName","sAMAccountName"
        $Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
        $Searcher.PageSize = 1000
        $Computers = $Searcher.findAll() | %{$_.properties[‘dnshostname’]}
        $Computers
    }
    function Ping-Server {
       Param([string]$server)
       $pingresult = Get-WmiObject win32_pingstatus -f "address=’$Server’"
       if($pingresult.statuscode -eq 0) {$true} else {$false}
    }

    if($FromAD){$computers = Get-ADComputers}
    else{if($list){$computers = get-Content $list}else{Write-Host "Please Provide List";return}}

    foreach($computer in $computers)
    {
        $Folder = "{2}\{1}-Logs-{0:MMddyymm}" -f [DateTime]::now,$computer,$backupLocation
        Write-Host "+ Processing Server $Computer"
        new-Item $folder -type Directory -force  | out-Null

        if(Ping-Server $computer)
        {
            Write-Host "  + Created Backup Folder $folder"
            $eventlogs = [System.Diagnostics.EventLog]::GetEventLogs($computer)
            foreach($log in $eventlogs)
            {
                $LogFile = "{0}\{1}.csv" -f $Folder,$log.Log
                Write-Host "  + Processing $($log.Log) Log"
                Write-Host "    – Backing up $($log.Log)"
                $logEntries = $log.Entries | %{"{0},{1},{2},{3},{4}" -f $_.TimeGenerated,$_.EntryType,$_.Source,$_.EventID,$_.Message}
                $logEntries | out-File $LogFile -enc ASCII -width 500
                Write-Host "    – Backed up to $logFile"
                Write-Host "    – Clearing Log $($Log.Log)"
                $log.Clear()
            }
            Write-Host
        }
        else
        {
            Write-Host "Server $Computer failed PING!" -foregroundcolor red
        }
    }

    For those that perfer EVT format and WMI…. I left the Clear part commented

    Param($BackupLocation,$list,$FromAD)

    function Get-ADComputers{
        $filter = "(&(objectcategory=computer))"
        $root = [ADSI]""
        $props = "dNSHostName","sAMAccountName"
        $Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
        $Searcher.PageSize = 1000
        $Computers = $Searcher.findAll() | %{$_.properties[‘dnshostname’]}
        $Computers
    }
    function Ping-Server {
       Param([string]$server)
       $pingresult = Get-WmiObject win32_pingstatus -f "address=’$Server’"
       if($pingresult.statuscode -eq 0) {$true} else {$false}
    }

    if($FromAD){$computers = Get-ADComputers}
    else{if($list){$computers = get-Content $list}else{Write-Host "Please Provide List";return}}

    foreach($computer in $computers)
    {
        if(ping-server $computer)
        {
            $Folder = "{1}-Logs-{0:MMddyymm}" -f [DateTime]::now,$computer
            Write-Host "+ Processing Server $Computer"
            New-Item "$backupLocation\$folder" -type Directory -force  | out-Null
            If(!(Test-Path "\\$computer\c$\LogBackups")){New-Item "\\$computer\c$\LogBackups" -type Directory -force | out-Null}
            $Eventlogs = Get-WmiObject Win32_NTEventLogFile -ComputerName $computer
            Foreach($log in $EventLogs)
            {
                $path = "\\{0}\c$\LogBackups\{1}.evt" -f $Computer,$log.LogFileName
                $result = ($log.BackupEventLog($path)).ReturnValue
                Copy-Item $path -dest "$backupLocation\$folder" -force
                #if($result -eq 0){$log.ClearEventLog()}
            }
        }
    }

    NOTE: Shortly after writing this… I found this little tibit… it seems to have been around since SP3 of Win2000

    Found it Here
    http://blogs.msdn.com/spatdsg/default.aspx

    AutoBackupLogFiles – backs up the event logs “Using this entry causes the Event Log service to automatically clear a full event log and to back up the log file. ”
    http://support.microsoft.com/kb/312571

    Get-ChildItem with Pretty Colors (UPDATED)

    Cedric Posted a message on the NG (news group) wanting a function like ls for linux. He wanted color while keeping width. I am sure there are other ways to do this, but what I did was created a script (I will convert to a function later) called Get-ChildItemPretty.ps1. The code is at the bottom, but I wanted to point out a few things.

    1) It has 3 Parameters and 1 switch
    – Dir: This is the Path you want to List (Defaults to $pwd)
    – Wide : How Many Colums Wide (Defaults to 3)
    – Pad : For Spacing on the Names (Defaults to 30)
    – Recurse : Switch to Recurse
    – Passtru : outputs objects
    2) By default this colors Folders Blue and Exe’s Red, but you could add what ever colors and extentions you want.
    3) I have thought about adding a Sort option to Sort by Name or by Type (what do you think?)
    4) You could add -full switch that would return fullname instead of just name.
    5) You could add -output switch that would return objects as well for pipeline parsing.

    ### UPDATED ###
    Working with /\/\0\/\/ (mostly MOW really) we came up with a better way using $host.UI
    This adds a PassTru to output objects

    # Colored wide directory list function
    #
    # /\/\o\/\/ 2007
    # www.thePowerShellGuy.com
    function List-Wide { param ($dir=$pwd,$Column=3,[Switch]$PassTru,[Switch]$recurse)
        $origFg = $host.ui.rawui.foregroundColor
        if($recurse)
        {
            $list = get-ChildItem $dir -recurse
        }
        else
        {
            $list = get-ChildItem $dir
        }
        foreach ($Item in $list)
        {
            Switch ($Item.Extension)
            {
                ".Exe" {$foregroundColor = "Yellow"}
                ".cmd" {$foregroundColor = "Green"}
                ".ps1" {$foregroundColor = "Red"}
                ".vbs" {$foregroundColor = "Red"}
                Default {$foregroundColor = $origFg}
            }
            if ($item.PSISContainer) {$foregroundColor = "Blue"}
            $max = \[int\]($Host.UI.RawUI.WindowSize.Width / $Column)
            $field = ($item.name.padRight($max).Substring(0,($Max -1)) + ‘ ‘)
            if ($item.name.length -gt $max -1)
            {
                $field = $field.Substring(0,($Max -3)) + ‘\*\*\*’
            }
            if ($Host.UI.RawUI.WindowSize.Width$host.UI.RawUI.CursorPosition.X -lt $max)
            {
                $host.ui.WriteLine()
            }
            $Host.UI.Write($foregroundColor,$host.ui.rawui.BackgroundColor,$field)
            if ($PassTru)
            {
                $Item
            }
        }
        $Host.UI.WriteLine()
    }

    Old Version

    							
    					
    					
    				

    Birth of a Script/Function

    I know my post have been few and far between, but I have been super swamped at work and will be for a few more months at least.

    That said I am writing some scripts and functions for work and as I was writing one today I though maybe I should share how I go about writing a function slash script. I like step-by-step lists so I decided at that format.

    1> A purpose
    – All Scripts must start with a purpose. Be it install something or check something. It needs to do something.

    2> I figure the best process to do what I want
    – Can what I want done be remoted?
    – Do I use WMI?
    – Do I use .NET?
    – Do I use external Commands?
    – Who is going to be doing it? What rights do they need?
    – Does it have potential to break stuff (BIGGY!!!)

    3> Write a function or functions that do what I want done on one Server, Device, or file
    Example

    function Checkhf{
       Param($srv,$ptch)
       $hf = get-WmiObject Win32_QuickfixEngineering -ComputerName $srv | where-Object{$_.HotfixID -match $ptch}
       if($hf)
       {
          return $true
       }
       else
       {
          return $false
       }
    }

    4> Cut and Paste function into my templates (At the bottom of post)
    – Add any missing parameters
    – Add any extra parsing
    – Add error control if needed

    5> Now it looks like this

    function Check-Hotfix {
        Param([string]$server,[string]$hotfix)
        Begin{
            function PingServer {
                param([string]$srv)
                $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
                if($pingresult.statuscode -eq 0) {$true} else {$false}
            }
            function Checkhf{
                Param($srv,$ptch)
                $hf = get-WmiObject Win32_QuickfixEngineering -ComputerName $srv | where-Object{$_.HotfixID -match $ptch}
                if($hf)
                {
                    return $true
                }
                else
                {
                    return $false
                }
            }
            Write-Host
            $process = @()
        }
        Process{
            if($_)
            {
                if($_.ServerName)
                {
                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            if($Server){$Process += $Server}
            if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
            foreach($s in $process)
            {
                if(PingServer $s)
                {
                    $myobj = "" | select-Object Server,Result
                    $myobj.Server = $s
                    $myobj.Result = Checkhf -srv $s -ptch $hotfix
                    Write-Output $myObj
                }
            }
        }
    }

    6> Final Test

    Here are my Templates
    – By default they take a Pipe, Server, or File and will process all three

    Template for Script

    Param([string]$server,[string]$file)
    Begin{
        function PingServer {
            param($srv)
            $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
            if($pingresult.statuscode -eq 0) {$true} else {$false}
        }
        function InsertFunctionHere{
        }
        Write-Host
        $process = @()
    }
    Process{
        if($_)
        {
            if($_.ServerName)
            {
                $process += $_.ServerName
            }
            else
            {
                $process += $_
            }
        }
    }
    End{
        # Check if $server is populated and add to Process Array
        if($Server){$Process += $Server}
        # Check if $file is populated and add to Process Array
        if($file)
        {
            foreach($entry in (get-content $file))
            {
                $Process += $entry
            }
        }
        # If Process Array is empty… Add self
        if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
        foreach($s in $process)
        {
            if(pingserver $s)
            {
                InsertFunctionHere $s
            }
            Write-Host
        }
    }

    Template for Function

    function Verb-Noun {
        Param([string]$server,[string]$file)
        Begin{
            function PingServer {
                param($srv)
                $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
                if($pingresult.statuscode -eq 0) {$true} else {$false}
            }
            function InsertFunctionHere{
            }
            Write-Host
            $process = @()
        }
        Process{
            if($_)
            {
                if($_.ServerName)
                {
                    $process += $_.ServerName
                }
                else
                {
                    $process += $_
                }
            }
        }
        End{
            # Check if $server is populated and add to Process Array
            if($Server){$Process += $Server}
            # Check if $file is populated and add to Process Array
            if($file)
            {
                foreach($entry in (get-content $file))
                {
                    $Process += $entry
                }
            }
            # If Process Array is empty… Add self
            if($process.Length -eq 0){$Process += get-content env:COMPUTERNAME}
            foreach($s in $process)
            {
                if(pingserver $s)
                {
                    InsertFunctionHere $s
                }
                Write-Host
            }
        }
    }

    UPDATED!!! Get-Uptime (The Custom Object extravaganza!!!)

    One of the most wonderful things about PowerShell is the ability to pass objects down the Pipe for further processing. In my first version of Get-Uptime I did not utilize this because I wanted to show what a huge difference it makes. I wanted to get this out pretty quick so I didn’t add the prettiness that I had in my first version, but it is easy enough to add.

    Here is the new Code. After the code I give some examples of how to use it.

    Function Get-Uptime{
        Param([string]$server)
        Begin {
            function PingServer {
                Param([string]$srv)
                $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
                if($pingresult.statuscode -eq 0) {$true} else {$false}
            }
            function myUptime {
                param([string]$srv)
                $os = Get-WmiObject Win32_OperatingSystem -ComputerName $srv
                $uptime = $os.LastBootUpTime
                return $uptime
            }
            function ConvertDate {
                param([string]$date,[string]$srv)
                $year = $date.substring(0,4)
                $Month = $date.Substring(4,2)
                $day = $date.Substring(6,2)
                $hour = $date.Substring(8,2)
                $min = $date.Substring(10,2)
                $sec = $date.Substring(12,2)
                $RebootTime = new-Object System.DateTime($year,$month,$day,$hour,$min,$sec)
                $now = [System.DateTime]::Now
                $uptime = $now.Subtract($RebootTime)
                $uptimeval = "$($uptime.days) days, $($uptime.Hours) hours, $($uptime.Minutes) minutes, $($uptime.seconds) seconds"
                $lastReboot = $rebootTime.toString()
                $sObject = new-Object -typename System.Object
                $sObject | add-Member -memberType noteProperty -name ServerName -Value $srv
                $sObject | add-Member -memberType noteProperty -name Days -Value $uptime.days
                $sObject | add-Member -memberType noteProperty -name Hours -Value $uptime.Hours
                $sObject | add-Member -memberType noteProperty -name Minutes -Value $uptime.Minutes
                $sObject | add-Member -memberType noteProperty -name Seconds -Value $uptime.seconds
                $sObject | add-Member -memberType noteProperty -name uptime -Value $uptimeval
                $sObject | add-Member -memberType noteProperty -name LastReboot -Value $rebootTime.ToUniversalTime()
                $sObject | add-Member -memberType noteProperty -name LastRebootUtc -Value $rebootTime.ToFileTimeUtc()
                write-Output $sObject
            }
            Write-Host
            $process = @()
            $objCollection = @()
        }
        Process {
            if($_){
                if($_.ServerName ){
                    $process += $_.ServerName
                }
                else{
                    $process += $_
                }
            }
        }
        End {
            if($Server){$process += $server}
            $i = 1
            foreach ($Server in $process){
                write-progress $Server "Total Progress->" -percentcomplete ($i/$process.length*100)
                if(PingServer $server){
                    $result = myUptime $server
                    $srvObject = ConvertDate $result $server
                    $objCollection += $srvObject
                }
                else {
                    Write-Host "Server [$server] not Pingable" -foregroundcolor red
                }
                $i = $i+1
            }
            Write-Output $objCollection
            Write-Host
        }
    }

    Examples:

    This get uptime on a Single Server

    get-uptime server | %{$_.uptime}

    This gets all servers up for more than 30 days

    $sl | Get-Uptime | ?{$_.Days -gt 30} | %{write-host "$($_.ServerName) :: $($_.uptime)"}

    This Displays the Last Reboot Time of a list of servers.

    $sl | get-uptime | %{Write-Host "Server $($_.ServerName) rebooted on $($_.LastReboot)"}

    The part of this I want to focus on is this

    $sObject = new-Object -typename System.Object
     $sObject | add-Member -memberType noteProperty -name ServerName -Value $srv
     $sObject | add-Member -memberType noteProperty -name Days -Value $uptime.days
     $sObject | add-Member -memberType noteProperty -name Hours -Value $uptime.Hours
     $sObject | add-Member -memberType noteProperty -name Minutes -Value $uptime.Minutes
     $sObject | add-Member -memberType noteProperty -name Seconds -Value $uptime.seconds
     $sObject | add-Member -memberType noteProperty -name uptime -Value $uptimeval
     $sObject | add-Member -memberType noteProperty -name LastReboot -Value $rebootTime.ToUniversalTime()
     $sObject | add-Member -memberType noteProperty -name LastRebootUtc -Value $rebootTime.ToFileTimeUtc()

    This is where I define my custom object. There are numerous ways to do this, but I chose this way. Notice the use of add-member cmdlets… It is extremely powerfull and extremely easy to use.

    Basically what I do is create a Generic Object $sObject. Add some noteProperties and populate them. Very simple, but as you can very “POWER”ful.

    Lee Holms has some excelent information on Custom Objects here:
    http://www.leeholmes.com/blog/AddCustomMethodsAndPropertiesToTypesInPowerShell.aspx

    I hope you enjoyed this new version… I currently actually keep both versions in my functions.ps1 file that I load in my profile. One as Get-Uptime and one as Get-UptimeExt. I get pretty and Smart 🙂

    Next »