Posts RSS Comments RSS 253 Posts and 411 Comments till now

Setting SNMP configuration in VMWare using Powershell

I have a ton of respect for the VMWare guys and the incredibly useful set of cmdlets they provide (FOR FREE) found HERE, but man have I been banging my head against the wall trying to simply set the SNMP configuration on my ESX hosts.

To help ease the pain for the REST of the world here are some thoughts/gotchas.

– You must have the object return from Get-VMHostSNMP to use Set-VMHostSNMP or Test-VMHostSNMP. Simply providing the name will not work
– You cannot use the name with Get-VMHostSNMP. It appears you must connect to the actual ESX host (using connect-viserver) and then run Get-VMHostSNMP
– Once you “get” the VMHostSNMP object you can reuse without worrying about the connect-viserver command.
– You need to add a ReadOnlyCommunity to each host
– You need to Enable traps with the -enable:$true parameter
– TargetCommunity, TargetPort, TargetHost parameters require that you also pass -addtarget
– You need to enable SNMP on the ESX firewall

You may find the script below useful for getting a VMHostSNMP object for each ESXHost on your Virtual Center

Using the script below you can then do things like
$SNMPHostObjects = .\Get-SNMPHost.ps1 $VirtualCenter

To Add a Community
$SNMPHostObjects | %{ Set-VMHostSnmp -HostSnmp $_ -ReadOnlyCommunity “YourCommunity”}

To Enable
$SNMPHostObjects | %{ Set-VMHostSnmp -HostSnmp $_ -Enabled:$true }

To Add a Target
$SNMPHostObjects | %{ Set-VMHostSnmp -HostSnmp $_ -TargetHost “1.1.1.1” -TargetCommunity “YourCommunity” -AddTarget }

To Test
$SNMPHostObjects | %{ Test-VMHostSnmp $_ }

To Remove a Target
$SNMPHostObjects | %{ Set-VMHostSnmp -HostSnmp $_ -TargetHost “1.1.1.1” -removeTarget}

Functions and why they are important!

I often get asked why I use functions and when I think they should be used. Below is a guide I wrote explaining my theory on functions.


Why use functions?

Functions are, generally speaking, small single task tools (like a flathead screwdriver or hammer.) They do one thing and they do that one thing reliably well. If you take this approach when writing code you will find it easier to debug and find yourself writing less code. Why less code? Because, you’ll find you are now able to port your functions from one script to another or possibly even in your day to day life.

How you decide to write a function?

I have three basic guidelines for when to write a function.

First, if I find I am repeating the same code block over and over (like a code block that checks several services on a computer.) It makes sense to just write a function to perform the check and then run that against each server. This allows me to trouble-shoot the code more efficiently.

Second, if I find that I can use this code in other scripts. For example if I write a nice recursive parsing block. I may want to reuse that logic in another script.

Finally, if I determine the code is useful outside of this script. This may seem like the previous guideline but it is slightly different. A good example here would be a ping-server function. Not only is this useful in other scripts, but it is also useful in my day to day life.

Do you always design functions with the idea of reuse in mind?

Generally speaking it is a good idea to ALWAYS consider reuse when writing code. This is paramount when working with functions. The sole purpose of functions in life is for reuse.  This is major consideration when designing your functions. Consider how and where they will be used. This will help establish what parameters it should have and what (if any) defaults it should have.

What do you put in your functions?

Ideally, because we design code for reuse, it is best to be as verbose as possible.  Basic rule of thumb is hardcode nothing, all data should be passed by parameters. Certainly you can have default values for the parameters, but allow the function caller to specific other options without having to modify the function.  This comes back to the black box approach. One needs to consider the effect of every change the original function and how that will affect the script as a whole.

In v1 I always try to implement –verbose and –whatif with my own switches. In v2 this is handled for you.

Do you separate the logic from the function?

When designing functions one should think about the looping and processing logic. Generally you will find this is script specific and should be implemented outside of the function. Ideally you would want to restrict logic to the party that requires the logic. For example, if you have logic to process servers, keep that logic outside of the functions. There is no need to implement that logic over and over for each function call. On the other hand, if you have logic that is expressly the domain of the function do not go crazy trying to rip it out just to put in the calling script.

What makes a good function?

Great functions are born out of need, but grow out of use. As you grow in your understanding your functions will grow with you. They are like the friend that is always there when you need them, but like that friend they need attention and care. Below are some features that functions should have:

Well defined parameters:

You function needs to be very clear on what data it expects. You accomplish this by having very specific parameters (this often will include the data type as well.) If you absolutely must have a specific value to process make sure that is clear from the function. A great way to accomplish this is by assigning the parameters default value to (Throw ‘$ThisParam is required’).

Consistent and expected output:

This is absolutely critical. You do not want to guess at what data will come from the function. You want the data to be what is expected. Design the function so that it returns one or more of a single data type (i.e. string, DateTime, or Boolean.) Be very cautious not to pollute the data stream with unexpected data written with write-output or data that wasn’t captured in a variable.

Self contained:

The function should NOT rely on any variables from the script. If the function needs input from outside make it a parameter.

Portability:

The single most important job of a function is to be portable. If you do not plan to reuse the code you might as well write the code inline. A key factor to portability is to make sure your variable names will not collide with the calling script. A good rule of thumb is to preface them with $my or $func (like $MyServer or $FuncServer.)

 

Getting users group membership (tokengroups)

Around the same time as I wrote my Linked-Value Attribute script I also came up with this little gem. It also uses a constructed attribute provided with Windows 2003 called “tokengroups.” (Did I mention this includes recursive groups?)

Effectively it gets the attribute which returns an array of SIDs (in byte array form) for each group. I then use a function I posted about eariler called ConvertTo-Name to convert that BYTE array into a friendly group name we humans like.

Parameters:

  • – Account: Can be User samAccountName or DN of the user
  • – Verbose: Enables verbose output

More Info:
You may notice the GetInfoEx call I make on the user object. This is because tokengroups is not an actual attribute and does not get “populated” until you specifically request it. The GetInfoEx does exactly that.

Links:
MSDN: GetInfoEx
MSDN: tokenGroups

Get-TokenGroups.ps1

Param($Account,[switch]$Verbose)

if($verbose){$verbosepreference="Continue"}

Write-Host
Write-Verbose " – Account: $Account"
Write-Verbose " – Verbose: $Verbose"

function ConvertTo-Name($sid,[switch]$FromByte) {
    if($FromByte)
    {
        $ID = New-Object System.Security.Principal.SecurityIdentifier($sid,0)
    }
    else
    {
        $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
    }
    if($ID)
    {
        $User = $ID.Translate([System.Security.Principal.NTAccount])
        $User.Value
    }
}
function GetDNfromName{
    Param($name)
    $root = [ADSI]""
    $filter = "(sAMAccountName=$name)"
    $props = @("distinguishedName")
    $Searcher = new-Object System.DirectoryServices.DirectorySearcher($root,$filter,$props)
    $Searcher.FindOne().properties.distinguishedname
}

if($Account -notmatch "CN=(.*),((OU|DC)=\w*)*")
{
    Write-Verbose " + Getting User DN for [$Account]"
    $Account = GetDNfromName $Account
    Write-Verbose "   – GetDNfromName returned [$Account]"
}

Write-Verbose " – Getting User Object"
$UserAccount = [ADSI]"LDAP://$Account"

Write-Verbose " – Calling GetInfoEx"
$UserAccount.GetInfoEx(@("tokengroups"),0)

Write-Verbose " – Getting tokengroups"
$groups = $UserAccount.Get("tokengroups")

Write-Verbose " + Processing Groups"
foreach($group in $groups)
{
    $GroupName = ConvertTo-Name $group -FromByte
    Write-Verbose "   – Found group [$GroupName]"
    $GroupName
}

Write-Host

Functions for Active Directory Permissions

Below are some of the function I have written to work with Access Control List in Active Directory.

Here is the list and what they do
Get-ADACL: Gets the Access Control List of an AD Object
Set-ADACL: Set the Access Control List of an AD Object
New-ADACE: Creates a Access Control Entry to add to an AD Object
ConvertTo-Sid: Converts a UserName to a SID
ConvertTo-Name: Converts a SID to the UserName
Get-Self: Gets current Security Identity


function Get-ADACL {
    Param($DNPath,[switch]$SDDL,[switch]$help,[switch]$verbose)
    function HelpMe{
        Write-Host
        Write-Host " Get-ADACL.ps1:" -fore Green
        Write-Host "   Gets ACL object or SDDL for AD Object"
        Write-Host
        Write-Host " Parameters:" -fore Green
        Write-Host "   -DNPath                : Parameter: DN of Object"
        Write-Host "   -sddl                  : [SWITCH]:  Output SDDL instead of ACL Object"
        Write-Host "   -Verbose               : [SWITCH]:  Enables Verbose Output"
        Write-Host "   -Help                  : [SWITCH]:  Displays This"
        Write-Host
        Write-Host " Examples:" -fore Green
        Write-Host "   Get ACL for ‘cn=users,dc=corp,dc=lab’" -fore White
        Write-Host "     .\Get-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’" -fore Yellow
        Write-Host "   Get SDDL for ‘cn=users,dc=corp,dc=lab’" -fore White
        Write-Host "     .\Get-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -sddl " -fore Yellow
        Write-Host
    }
   
    if(!$DNPath -or $help){HelpMe;return}
   
    Write-Host
    if($verbose){$verbosepreference="continue"}
   
    Write-Verbose " + Processing Object [$DNPath]"
    $DE = [ADSI]"LDAP://$DNPath"
   
    Write-Verbose "   – Getting ACL"
    $acl = $DE.psbase.ObjectSecurity
    if($SDDL)
    {
        Write-Verbose "   – Returning SDDL"
        $acl.GetSecurityDescriptorSddlForm([System.Security.AccessControl.AccessControlSections]::All)
    }
    else
    {
        Write-Verbose "   – Returning ACL Object [System.DirectoryServices.ActiveDirectoryAccessRule]"
        $acl.GetAccessRules($true,$true,[System.Security.Principal.SecurityIdentifier])
    }
}
function Set-ADACL {
    Param($DNPath,$acl,$sddl,[switch]$verbose,[switch]$help)
    function HelpMe{
        Write-Host
        Write-Host " Set-ADACL.ps1:" -fore Green
        Write-Host "   Sets the AD Object ACL to ‘ACL Object’ or ‘SDDL’ String"
        Write-Host
        Write-Host " Parameters:" -fore Green
        Write-Host "   -DNPath                : Parameter: DN of Object"
        Write-Host "   -ACL                   : Parameter: ACL Object"
        Write-Host "   -sddl                  : Parameter: SDDL String"
        Write-Host "   -Verbose               : [SWITCH]:  Enables Verbose Output"
        Write-Host "   -Help                  : [SWITCH]:  Displays This"
        Write-Host
        Write-Host " Examples:" -fore Green
        Write-Host "   Set ACL on ‘cn=users,dc=corp,dc=lab’ using ACL Object" -fore White
        Write-Host "     .\Set-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -ACL $acl" -fore Yellow
        Write-Host "   Set ACL on ‘cn=users,dc=corp,dc=lab’ using SDDL" -fore White
        Write-Host "     .\Set-ADACL.ps1 ‘cn=users,dc=corp,dc=lab’ -sddl `$mysddl" -fore Yellow
        Write-Host
    }
   
    if(!$DNPath -or (!$acl -and !$sddl) -or $help){HelpMe;Return}
   
    Write-Host
    if($verbose){$verbosepreference="continue"}
    Write-Verbose " + Processing Object [$DNPath]"
   
    $DE = [ADSI]"LDAP://$DNPath"
    if($sddl)
    {
        Write-Verbose "   – Setting ACL using SDDL [$sddl]"
        $DE.psbase.ObjectSecurity.SetSecurityDescriptorSddlForm($sddl)
    }
    else
    {
        foreach($ace in $acl)
        {
            Write-Verbose "   – Adding Permission [$($ace.ActiveDirectoryRights)] to [$($ace.IdentityReference)]"
            $DE.psbase.ObjectSecurity.SetAccessRule($ace)
        }
    }
    $DE.psbase.commitchanges()
    Write-Host
}
function New-ADACE {
    Param([System.Security.Principal.IdentityReference]$identity,
        [System.DirectoryServices.ActiveDirectoryRights]$adRights,
        [System.Security.AccessControl.AccessControlType]$type,
        $Guid)
   
    $help = @"
    $identity
        System.Security.Principal.IdentityReference
        http://msdn.microsoft.com/en-us/library/system.security.principal.ntaccount.aspx
   
    $adRights
        System.DirectoryServices.ActiveDirectoryRights
        http://msdn.microsoft.com/en-us/library/system.directoryservices.activedirectoryrights.aspx
   
    $type
        System.Security.AccessControl.AccessControlType
        http://msdn.microsoft.com/en-us/library/w4ds5h86(VS.80).aspx
   
    $Guid
        Object Type of the property
        The schema GUID of the object to which the access rule applies.
"
@
    $ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity,$adRights,$type,$guid)
    $ACE
}
function ConvertTo-Sid($UserName,$domain = $env:Computername) {
   $ID = New-Object System.Security.Principal.NTAccount($domain,$UserName)
   $SID = $ID.Translate([System.Security.Principal.SecurityIdentifier])
   $SID.Value
}
function ConvertTo-Name($sid) {
   $ID = New-Object System.Security.Principal.SecurityIdentifier($sid)
   $User = $ID.Translate( [System.Security.Principal.NTAccount])
   $User.Value
}
function Get-Self{
   ([Security.Principal.WindowsIdentity]::GetCurrent())
}

Test-CitrixHotfix

I often find the need to compare the state of my PS servers. This little script allows me to find all the Citrix hotfixes for a group of server(s), or list of server(s) that have a specific hotfix.

Parameters:
$File: The name of the file which contains a list of servers
$Filter: Name or Regex of the Hotfix(es) to return
$Server: Name of the server to check

What it returns (a custom object):
ServerName: Name of the Server with the Hotfix(es)
Hotfixes: List of hotfix names [String[]]
RawObjects: Array of MFCOM MetaFrameHotfix objects

Examples
To List all the Hotfixes on a list of servers
.\Test-CitrixHotfix.ps1 ServerList.txt

To List a Specific hotfix on a list of servers
.\Test-CitrixHotfix.ps1 ServerList.txt -filter PSE450W2K3021

To List all the hotfixes on a specific Server
.\Test-CitrixHotfix.ps1 -s Server1

To List a specific hotfix for a specific server
.\Test-CitrixHotfix.ps1 -s Server1 -filter PSE450W2K3021

The Code:

Param($file,$filter=".*",$server)

Function Ping-Server {
   Param([string]$server)
   $pingresult = Get-WmiObject win32_pingstatus -f "address=’$Server’"
   if($pingresult.statuscode -eq 0) {$true} else {$false}
}
Function Check-CitrixHotfix{
    Param([string]$server)
    $mf = New-Object -ComObject MetaframeCOM.MetaframeFarm
    $mf.Initialize(1)
    $srv = $mf.GetServer2(6,$server)
    $list = $srv.winServerObject2.hotfixes | ?{$_.Name -match $filter}
    $list
}

if($file -and (Test-Path $file))
{
    $servers = cat $file
}
if($input)
{
    $servers = $input
}

if($server){$servers += $server}

foreach($srv in $servers)
{
    if(Ping-Server $srv)
    {
        $hotfixes = Check-CitrixHotfix $srv
        $myobj = "" | Select-Object ServerName,Hotfixes,RawObjects
        $myobj.ServerName = $srv
        $myobj.Hotfixes   =  $hotfixes | %{$_.Name}
        $myobj.RawObjects = $hotfixes
        $myobj
    }
    else
    {
        write-host $srv
        write-host "————"
        write-host "Server not pingable"
    }
}

Tracing LDAP calls with Powershell

Spat had an eerily coincidental blog post the other day (HERE). The reason I say eerily is because the night before I was fighting trying to get a LDAP trace, this trace was to help figure out EXACTLY how SDS.ActiveDirectory got replication cursors from a Domain Controller (another joe Richards discussion.) Anyway, I digress, I found the blog entry EXTREMELY useful as it allowed me to get what I needed. I proceeded to leave a comment suggesting that this looked like a good job for Powershell as the resulting file from the tool is a CSV. This has led to a “challenge” from Spat and this is my response. I hope I did it justice.

Useful Links about Tracelog.exe
———–
Details about TraceLog.exe
LDAP tracing with TraceLog
ADSI tracing with TraceLog

Details about Script

Here are the functions in the script

Trace-Log
-flag: Hex value for the flags you want to pass (Default Value = “0x1FFFDFF3”)
-guid: GUID or File for tracing (Default Value = “LDAP”)
-SessionName: Unique Name for the actual trace (Default Value = “mytrace”)
-exe: The full name with extension of the EXE to add to registry to enable tracing. This only has to be done the first time you want to trace for an EXE.
[switch]Start: If set it enables logging. If not set, logging is disabled.
[switch]ADSI: If set it passes the ADSI GUID for tracing
[switch]LDAP: If set it passes the ADSI GUID for tracing

Convert-TraceLog
-Source: Trace (etl) file to convert to csv (Default Value = “.\ldap.etl”)
-file: File to set the results to (Default Value = “TraceResults.csv”)
[switch]$import: If set it will return a custom object with results

Below is a video that shows a demo of the script in use. I hope to do another one of these showing how to trace ADSI as well as LDAP. Make sure to read the Comments in Green. I tried to allow enough time. You can click to pause.

Download Tracelog Transcript (right click | Save Target As…)
Best Viewed Full Screen

Get the Flash Player to see this content.

Code
Download Trace Log Functions (right click | Save Target As…)

function Trace-Log {
    Param($file = ".\ldap.etl",
        $flag = 0x1FFFDFF3,
        $guid = "LDAP",
        $SessionName = "mytrace",
        $exe,
        [switch]$start,
        [switch]$ADSI,
        [switch]$LDAP
    )
    if($ADSI){$guid = "ADSI"}
    switch -exact ($guid)
    {
        "LDAP"  {$myguid = ‘#099614a5-5dd7-4788-8bc9-e29f43db28fc’}
        "ADSI"  {$myguid = ‘#7288c9f8-d63c-4932-a345-89d6b060174d’}
        Default {$myguid = "’$_’"}
    }
   
    Write-Host
   
    if($start)
    {
        Write-Host " Action: Start" -fore Yellow
        Write-Host " GUID:   $GUID" -fore Yellow
        Write-Host " File:   $file" -fore Yellow
        Write-Host " Flag:   $flag" -fore Yellow
        if($exe){Write-Host " Exe:    $exe" -fore Yellow}
       
    }
    else
    {
        Write-Host " State: Disabled" -fore Red
    }
   
    Write-Host
   
    if(!(test-Path "HKLM:\System\CurrentControlSet\Services\ldap\tracing\$exe") -and $exe)
    {
        new-Item -path "HKLM:\System\CurrentControlSet\Services\ldap\tracing" -name $exe | out-Null
    }
   
    if($start)
    {
        $cmd = "Tracelog.exe -start ‘$SessionName’ -f ‘$file’ -flag ‘$flag’ -guid ‘$myguid’"
    }
    else
    {
        $cmd = "tracelog -stop $SessionName"
    }
   
    Write-Host
    Write-Host "==========================" -fore White -back black
    write-Host "Running Command:" -fore White
    Write-Host " ==> $cmd" -fore Yellow
    invoke-Expression $cmd
    Write-Host "==========================" -fore White -back black
    Write-Host
}
function Convert-TraceFile{
    Param($Source=".\ldap.etl",$file="TraceResults.csv",[switch]$import)
   
    $cmd = "tracerpt.exe $Source -o $file -of CSV -y"
   
    invoke-Expression $cmd
   
    if($import)
    {
        import-Csv $file
    }
}

userAccountControl and “User cannot change password”

Someone asked me a question about setting the “User cannot change password” check box in ADUC. They were creating the user account and setting PASSWD_CANT_CHANGE along with other settings (see my post about HERE about userAccountControl and values) and they couldn’t figure out why the check box wasn’t being applied.

I thought about this, and my first impression was they had the wrong bit value. So, I posted the “correct” one. They came back and said “That didn’t work.”

Hmmm, that’s curious. I turned to Dean and asked him if I was missing something obvious (I had a nagging feeling I had been here before) and he informed me that it has to be set with an “Extended Right” (control access right) via an ACE.

DOH! Now the whole scenario I had THAT feeling about came back to me. I recall having this discussion with someone and providing them a Script that would set the ACE. I searched for the script and I couldn’t find it. This happens to me a lot so I decided a while ago… when I run across this again, BLOG IT!

Technical Info:

In the past, permissions on the ‘userAccountControl’ attribute could be edited oftentimes making the effective password policy moot, i.e. you could end up with accounts that don’t comply with the domain’s password policy.

In Windows 2000, you can’t easily prevent this except by using third party front-end/provisioning tools to manage user objects. In Windows 2003 and later, you can use three newly-added extended rights (Control Access Rights) to prevent these bits from being edited even when the caller has permission to do so. The three (new) ‘Extended Rights’ are –

• Update password not required bit [controls ‘password not required’ and maps to ACE in footnote below]
• Enable per user reversible encryption [controls whether password is stored reversibly encrypted or not]
• Unexpire password [controls ‘password never expires’]

Each of these extended rights MUST be configured on the domain head and scoped as “This Object only” with ALLOW or DENY for the security principals you designate. By default, ‘Authenticated Users’ is granted an ALLOW ACE for each of the three extended rights. This doesn’t mean any old authenticated user can alter the password related bits in the ‘userAccountControl’ attribute; they still require the permission to modify ‘userAccountControl’.

USAGE SCENARIO – create a single group representing ALL three extended rights (or perhaps ONE group for EACH extended right). Then ACL the group(s) accordingly on the domain head with a DENY ACE. Finally, place the account-administrator users and groups that have management permissions to user objects (i.e. they have write permissions to the ‘userAccountControl’ property) in the group(s) you just created thereby preventing those account-administrators from altering the password related bits on the ‘userAccountControl’ attribute resulting in an enforced password policy.

IMPORTANT NOTE [Observed Behavior] – When viewing or changing a user’s ability to change their own password (User Cannot Change Password) through the GUI, it no longer appears to touch ‘userAccountControl’s bit 0x40 (64) — rather, it simply grant’s the ‘SELF’ security principal ‘ALLOW’ or ‘DENY’ to ‘Change Password’ — this can be easily verified by viewing the DACL.

Links:
Modifying User Cannot Change Password (LDAP Provider)

So… Here it is (Set-UserCannotChangePassword.ps1)
Parameters
User: The sAMAccountName of the User
CheckBox: If passed it checks the box
Default: Remove Check box.

Param($User = $(throw ‘$User is Required’,[switch]$CheckBox)
Write-Host

$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"","(&(objectcategory=User)(sAMAccountName=$user))")
$MyUser = $Searcher.FindOne().GetDirectoryEntry()

if(!$?){" !! Failed to Get User !!";Return}

if($CheckBox)
{
    Write-Host " – Checking Box for User [$($MyUser.distinguishedName)]"
    $self = [System.Security.Principal.SecurityIdentifier]‘S-1-5-10’
    $ExtendedRight = [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight
    $deny = [System.Security.AccessControl.AccessControlType]::Deny
    $selfDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule($self,$ExtendedRight,$deny,‘ab721a53-1e2f-11d0-9819-00aa0040529b’)
    $MyUser.psbase.get_ObjectSecurity().AddAccessRule($selfDeny)
    $MyUser.psbase.CommitChanges()
}
else
{
    Write-Host " – Removing Check Box for User [$($MyUser.distinguishedName)]"
    $ACL = $MyUser.psbase.get_ObjectSecurity().GetAccessRules($true,$false, [System.Security.Principal.NTAccount])
    $ACEs = $ACL | ?{($_.ObjectType -eq ‘ab721a53-1e2f-11d0-9819-00aa0040529b’) -and ($_.AccessControlType -eq ‘Deny’)}
    foreach($ACE in $ACEs){if($ACE){[void]$MyUser.psbase.get_ObjectSecurity().RemoveAccessRule($ACE)}}
    $MyUser.psbase.CommitChanges()
}

Write-Host

What Are “Parameterized Properties?”

I recently had someone ask me a question about property types. They kept referring to Parameterized Properties and while I knew they were confused, I only had vague understanding of this concept and I knew I definitely could not define it.

I had tried google and all I came up with was this ETS Parameterized Properties. I shot this description over to a friend at MS and he said that was a bad definition and one didn’t really exist. I asked him to define it the best he can and here is the result. I would consider this source pretty authoritative 🙂

Note: These are very common with COM Objects

Parameterized Property

Properties are members that are accessed like fields but are actually methods. There is a setter method for setting the value of the property and a getter method for getting the current value of the property. Normally, the setter method takes only one parameter (the value to set) and the getter does not take any.

However, there are situations where additional parameters are needed. Consider the case where you want to use the property to get and set a member of a collection. To do this, the setter method requires 2 parameters—the index (location) in the collection and the value to set the member to; the getter method requires 1 parameter—the index to retrieve the value from. When a property requires additional parameters to operate, it is called a parameterized property or an indexer.

Test-Host (WMI Ping -or Port Check)

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

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

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

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

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

A Demo of the script in action

Best Viewed Full Screen

Get the Flash Player to see this content.


Script CODE

Param($property,$tport=135,$timeout=1000,[switch]$port,[switch]$verbose)
Begin{
    function TestPort {
        Param($srv)
        $error.Clear()
        $ErrorActionPreference = "SilentlyContinue"
        $tcpclient = new-Object system.Net.Sockets.TcpClient
        $iar = $tcpclient.BeginConnect($srv,$tport,$null,$null)
        $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
        # Traps    
        trap {if($verbose){Write-Host "General Exception"};return $false}
        trap [System.Net.Sockets.SocketException]
        {
            if($verbose){Write-Host "Exception: $($_.exception.message)"}
            return $false
        }
        if(!$wait)
        {
            $tcpclient.Close()
            if($verbose){Write-Host "Connection Timeout"}
            return $false
        }
        else
        {
            $tcpclient.EndConnect($iar) | out-Null
            $tcpclient.Close()
        }
        if(!$error[0]){return $true}
    }
    function PingServer {
        Param($MyHost)
        $pingresult = Get-WmiObject win32_pingstatus -f "address=’$MyHost’"
        if($pingresult.statuscode -eq 0) {$true} else {$false}
    }
}
Process{
    if($_)
    {
        if($port)
        {
            if($property)
            {
                if(TestPort $_.$property){$_}  
            }
            else
            {
                if(TestPort $_){$_}
            }
        }
        else
        {
            if($property)
            {
                if(PingServer $_.$property){$_}  
            }
            else
            {
                if(PingServer $_){$_}
            }
        }
    }
}

If you want this as a function use this code

function Test-Host{
Param($property,$tport=135,$timeout=1000,[switch]$port,[switch]$verbose)
Begin{
    function TestPort {
        Param($srv)
        $error.Clear()
        $ErrorActionPreference = "SilentlyContinue"
        $tcpclient = new-Object system.Net.Sockets.TcpClient
        $iar = $tcpclient.BeginConnect($srv,$tport,$null,$null)
        $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
        # Traps    
        trap {if($verbose){Write-Host "General Exception"};return $false}
        trap [System.Net.Sockets.SocketException]
        {
            if($verbose){Write-Host "Exception: $($_.exception.message)"}
            return $false
        }
        if(!$wait)
        {
            $tcpclient.Close()
            if($verbose){Write-Host "Connection Timeout"}
            return $false
        }
        else
        {
            $tcpclient.EndConnect($iar) | out-Null
            $tcpclient.Close()
        }
        if(!$error[0]){return $true}
    }
    function PingServer {
        Param($MyHost)
        $pingresult = Get-WmiObject win32_pingstatus -f "address=’$MyHost’"
        if($pingresult.statuscode -eq 0) {$true} else {$false}
    }
}
Process{
    if($_)
    {
        if($port)
        {
            if($property)
            {
                if(TestPort $_.$property){$_}  
            }
            else
            {
                if(TestPort $_){$_}
            }
        }
        else
        {
            if($property)
            {
                if(PingServer $_.$property){$_}  
            }
            else
            {
                if(PingServer $_){$_}
            }
        }
    }
}}

Set-CitrixServerLogon.ps1 (Citrix Top 10)

Here is a useful little script. This Creates a MFCom Server Object and disables or Enables Logons for that Server.

# Set-CitrixServerLogon.ps1
# Brandon Shell [MVP]
# www.bsonposh.com
# Sets the Server to Enable or Disable Logons
Param($Server,[switch]$enable,[switch]$disable,[switch]$help)
function HelpMe{
    Write-Host
    Write-Host " Set-CitrixServerLogon.ps1:" -fore Green
    Write-Host "   Sets the Server to Enable or Disable Logons"
    Write-Host
    Write-Host " Parameters:" -fore Green
    Write-Host "   -Server                  : Optional. Server to Set Logon"
    Write-Host "   -Enable                  : Optional. Checks Hours of Idle Time (Default)"
    Write-Host "   -Disable                 : Optional. Checks Minutes of Idle Time"
    Write-Host "   -Help                    : Optional. Displays This"
    Write-Host
    Write-Host " Examples:" -fore Green
    Write-Host "   To disable the Logon for a Server" -fore White
    Write-Host "     Set-CitrixServerLogon.ps1 -server <serverName> -Disable" -fore Yellow
    Write-Host
}

if(!$Server -or $help){helpme;Write-Host;return}

Write-Host

Write-Host " Getting Server [$Server]"
$mfsrv = New-Object -ComObject MetaFrameCOM.MetaFrameServer

Write-Host " – Initializing Server"
$mfsrv.Initialize(6,$Server)

if($enable)
{
    Write-Host " – Setting to EnableLogon = 1"
    $mfSrv.WinServerObject.EnableLogon = 1
}
if($disable)
{
    Write-Host " – Setting to EnableLogon = 0"
    $mfSrv.WinServerObject.EnableLogon = 0
}

Write-Host " – Server [$($mfSrv.ServerName)] is set to [$($mfSrv.WinServerObject.EnableLogon)] for EnableLogon"

Write-Host

Another option would be to remove the Apps from the Server all together.

# Unpublish-CitrixServer.ps1
# Brandon Shell [MVP]
# www.bsonposh.com
# Removes all App from Server
Param($Server)
$mfsrv = New-Object -ComObject MetaFrameCOM.MetaFrameServer
$mfsrv.Initialize(6,$Server.ToUpper())
$mfsrv | foreach{$_.Applications} | foreach{$_.LoadData(1);$_.RemoveServer($Server.ToUpper());$_.SaveData()}

Next »