Posts RSS Comments RSS 253 Posts and 408 Comments till now

Its been awhile… what have you been up to?

Sorry I haven’t been blogging much. While I have been very busy at work my Powershell exploits have not been idle :)

Here are some updates :)

Anyway… I just wanted to give you a heads up on what was going on with me. Shortly I plan to have a weekly or monthly blog post covering my BSonPosh module which is up to ~140 cmdlets.

blog: Breaking down DCDiag.exe to an object

Using some regex magic and some custom object mojo I threw together this DCDiag objectifier.

These are very early bits (only spent a few minutes on it) and may not go any further as I think this would be better as a cmdlet, but it was fun to play with and show some of the POWER you have at your finger tips.

The Code

$DCDiag = Get-Content c: empdcdiag.txt

# Creating the DCDiag object
$DCDiagObject = "" | Select-Object Server,Advertising,SPNs,KnownRoles,Tests
$DCDiagObject.Server = ""
$DCDiagObject.Advertising = @()
$DCDiagObject.KnownRoles = @()
$DCDiagObject.SPNs = @()
$DCDiagObject.Tests = @()

# Setting up RegEXs here so they are easier to consume for the viewer
$DCNameRegex = "^s** Connecting to directory service on server (?<DCName>w+)."
$Advertising = "^s+ThesDCsw*sissadvertisingsass(a|an|having a)s(?<Type>.*)"
$KnownRolesRegex = "^.*Roles(?<Role>.*)sOwner = CN=NTDS Settings,CN=(?<Holder>w*),"
$SPNsRegex = "^s+* SPN found :(?<SPN>.*)"
$StartRegex = "^s*.+s(?<Target>w+)s(?<Result>w+)stests(?<Test>w+)"

# Getting stuff done
switch -regex ($DCDiag)
{
    $DCNameRegex        {$DCDiagObject.Server = $matches.DCName}
    $Advertising        {$DCDiagObject.Advertising += $matches.Type}
    $KnownRolesRegex    {$DCDiagObject.KnownRoles += $matches.Role}
    $SPNsRegex          {$DCDiagObject.SPNs += $matches.SPN}
    $StartRegex         {
                            $myobj = "" | Select-Object Target,Test,Result
                            $myobj.Target = $matches.Target
                            $myobj.Test = $matches.Test
                            $myobj.Result = $matches.Result
                            $myobj | Add-Member -MemberType ScriptMethod -name ToString -value {$this.Test} -force
                            $DCDiagObject.Tests += $myobj
                        }
}

# outputting object
$DCDiagObject

Breaking down DCDiag.exe to an object

Using some regex magic and some custom object mojo I threw together this DCDiag objectifier.

These are very early bits (only spent a few minutes on it) and may not go any further as I think this would be better as a cmdlet, but it was fun to play with and show some of the POWER you have at your finger tips.

The Code

$DCDiag = Get-Content c:\temp\dcdiag.txt

# Creating the DCDiag object
$DCDiagObject = "" | Select-Object Server,Advertising,SPNs,KnownRoles,Tests
$DCDiagObject.Server = ""
$DCDiagObject.Advertising = @()
$DCDiagObject.KnownRoles = @()
$DCDiagObject.SPNs = @()
$DCDiagObject.Tests = @()

# Setting up RegEXs here so they are easier to consume for the viewer
$DCNameRegex = "^\s*\* Connecting to directory service on server (?<DCName>\w+)."
$Advertising = "^\s+The\sDC\s\w*\sis\sadvertising\sas\s(a|an|having a)\s(?<Type>.*)"
$KnownRolesRegex = "^.*Role\s(?<Role>.*)\sOwner = CN=NTDS Settings,CN=(?<Holder>\w*),"
$SPNsRegex = "^\s+\* SPN found \:(?<SPN>.*)"
$StartRegex = "^\s*\.+\s(?<Target>\w+)\s(?<Result>\w+)\stest\s(?<Test>\w+)"

# Getting stuff done
switch -regex ($DCDiag)
{
    $DCNameRegex        {$DCDiagObject.Server = $matches.DCName}
    $Advertising        {$DCDiagObject.Advertising += $matches.Type}
    $KnownRolesRegex    {$DCDiagObject.KnownRoles += $matches.Role}
    $SPNsRegex          {$DCDiagObject.SPNs += $matches.SPN}
    $StartRegex         {
                            $myobj = "" | Select-Object Target,Test,Result
                            $myobj.Target = $matches.Target
                            $myobj.Test = $matches.Test
                            $myobj.Result = $matches.Result
                            $myobj | Add-Member -MemberType ScriptMethod -name ToString -value {$this.Test} -force
                            $DCDiagObject.Tests += $myobj
                        }
}

# outputting object
$DCDiagObject

More Powershell RegEx fun (playing with ‘route Print’)

A problem come that required me to check the routing table on a large number of machines. My first thought was to try to find a .NET class to dump the table, but alas I could not find any class to do what I wanted. That was a bummer but not unexpected so I decided to just use ol’faithful psexec.exe (Found here.)

Now, for those of you that have used ‘route print’ before you know the output is well… verbose :) and clearly that would not be very helpful for filtering.


Enter ‘Get-RouteTable” (found below)

I share this, not so much because I found it incredibly useful (although I did,) but because it illustrates what I have found to be a great way of creating complex Regular Expressions (for the sake of brevity I will use RegEX for the rest of the article.)

My approach is quiet simple. Create a string object for the RegEx and build the Regex over time testing each section.

Lets take this string for example

Active Routes:
Network Destination        Netmask                 Gateway          Interface            Metric
          0.0.0.0                   0.0.0.0                    192.168.1.1     192.168.1.13     25
        127.0.0.0                 255.0.0.0                127.0.0.1          127.0.0.1           306
        127.0.0.1                 255.255.255.255    127.0.0.1           127.0.0.1           306

I wanted to extract the Network Destination, Netmask, Gateway, Interface, and Metric and make them properties on a custom object. How does one go about that? One way is to create a regular expression with labels and use $matches to extract the information. This is route I took, but instead of trying to write a single complex RegEx and then trouble-shooting it for hours I decided to break down the regular expression into bite size chunks.

To show you what I mean by complex… here is the RegEx you end up with

“^\s+(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Gateway>\d{1,3
}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+(?<Metric>\d{1,4})$”

As you can this is fairly complex and could be a HUGE pain to trouble shoot.

Following my approach you break down. First you add the begin and end to the regex

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “.+$”

The next step is we add a label and pattern for the “Network Destination” and test

$DiscoverRoute  = “^\s+”

$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”

$DiscoverRoute += “.+$”

(route print) | ?{$_ -match $DiscoverRoute}

If you get results you expect the next step is to make sure $matches sees the labels as you expect

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Great! Now we can the next part

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+$”

Repeat the Last test but make sure you have a value for both Network and NetMask

(route print) | ?{$_ -match $DiscoverRoute} | %{$matches}

Rinse and repeat until you end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})”
$DiscoverRoute += “.+$”

This does not work just yet… can you spot why? The reason is because we have been adding “.+$” (zero or more of anything) to the end every time for testing purposes. We now need to remove this and add the $ to the end of the Metric string because the EOL is immediately after the last digit in Metric column. We end up with this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

We took a complex RegEx and with a little string concantination we were able to break it down and make the task quite simple.

Note: Another cool effect of this method is that we can easily comment out a section that maybe break the entire regular expression like this

$DiscoverRoute  = “^\s+”
$DiscoverRoute += “(?<Network>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<NetMask>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
#$DiscoverRoute += “(?<Gateway>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “.+(?<Interface>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+”
$DiscoverRoute += “(?<Metric>\d{1,4})$”

Just make sure that you add the “.+” to the line just after the comment line. This allows the regular expression to replace that section with basically anything.

NOTE: A final, slightly embarrassing, note. After writing the script below I realized this information (save the persistent routes) is readily available using WMI via the Win32_IP4RouteTable class.

The Code:


Param($Servers)

function Convert-RouteTabletoObject ($data)
{
    # RegEx for Discovery Routes
    $DiscoverRoute  = "^\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $DiscoverRoute += "(?\d{1,4})"
    $DiscoverRoute += "$"

    # RegEx for Persistent Routes
    $PersistentRoute  = "^\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+"
    $PersistentRoute += "(?\d{1,4})"
    $PersistentRoute += "$"

    $Routes = @()
    switch -regex ($data)
    {
        $DiscoverRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = $matches.Interface
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $False
                        $Routes += $NewRoute
                    }
        $PersistentRoute
                    {
                        $NewRoute = "" |Select-Object Network,Netmask,Gateway,Interface,Metric,Persistent
                        $NewRoute.Network = $matches.Network
                        $NewRoute.Netmask = $matches.Netmask
                        $NewRoute.Gateway = $matches.Gateway
                        $NewRoute.Interface = "N/A"
                        $NewRoute.Metric = $matches.Metric
                        $NewRoute.Persistent = $True
                        $Routes += $NewRoute
                    }
    }
    $Routes
}

foreach($server in $servers)
{
    $myobj = "" | Select-Object Name,RouteTable
    $myobj.Name = $Server
    $myobj.RouteTable = Convert-RouteTabletoObject (invoke-Expression "psexec \\$server route print" 2&gt;$NULL)
    $myobj
}

# Note: You can use WMI to get most of this info – Get-WMIObject Win32_IP4RouteTable -ComputerName

MS Cluster fun with Powershell (super cool)

A little while back Brad and I wrote a script to compare files between Microsoft Clusters.

After a little back and forth we came up with the script posted here: Check that driver file versions match on all your cluster nodes via Powershell

It is very cool.

Fun with Active Directory (Playing Around Series)

This is the first in a series of posts call “Playing Around Series.” This series will basically be demo Videos of different Snapins/.NET Classes and their use.

In this entry I run through creating a DirectoryEntry, DirectorySearcher, and using the System.DirectoryServices.ActiveDirectory.Domain Class.

Note: This is a fast run through. I STRONGLY recommend pausing and reading the Comments. Best Viewed Full Screen

Get the Flash Player to see this content.


Special Shout-out to JayKul and Jeffrey Snover for the Start-Demo script.

http://www.powershellcentral.com/scripts/302

Demo Text

#
# Lets start off by looking at DirectoryEntry
#
$DE = New-Object System.DirectoryServices.DirectoryEntry("LDAP://CN=tstUsr101,OU=MyUsers,DC=corp,DC=lab")
#
# First lets see what we have access to
#
$DE | Get-Member
#’
# Hmmm.. doesn’t seem like much. OH WAIT! Remember Powershell abstracts the class… Lets add psbase
#
$DE.psbase | Get-Member
#
# Lets look at what properties are available.
#
$DE.psbase.Properties
#
# Thats more like it. You may also note that some AD properties are still missing.
# That is because that LDAP doesnt return all the properties. For these you need to "GET" them.
$DE.psbase.InvokeGet(‘msExchUMFaxID’)
#
# Using DirectoryEntry is fine if you know the DN of the object, but what if you need to search?
# Lets look at System.DirectoryServices.DirectorySearcher
#
# The Searcher needs some info so put that in variables first
#
$root = [ADSI]""  ## This is using the Type Accelerator we spoke about earlier… This is Gets the base
$filter = "(&(objectcategory=user))"
#
# Now Lets create the searcher
#
$searcher = New-Object System.DirectoryServices.DirectorySearcher($root,$filter)
#
# That gets the searcher ready, but to execute we need to call findall() or findone()
#
$users = $searcher.findAll()
#
# Lets see what we got. We have alot so lets only pick the first 10
#
$users | select -first 10
#
# Tons of info, but notice that this is NOT the same as DirectoryEntry
#
$users | get-Member
#
# It still has the properties property, Lets look (but only the first 3)
#
$users | select -first 3 | %{$_.Properties}
#
# Finally Lets look at System.DirectoryDervices.ActiveDirectory.Domain
#
# We can use this to interactively browse around
#
[system.directoryservices.activedirectory.domain]::GetCurrentDomain()
#
# Lets assign that to variable to play with
#
$domain = [system.directoryservices.activedirectory.domain]::GetCurrentDomain()
$domain
#
# Lets see what this has to offer
#
$domain | get-member
#
# Tons of cool stuff here.
#
# We can find all domain controllers
$domain.FindAllDomainControllers()
#
# We Can look at our Domain FSMO
#
$domain | ft PdcRoleOwner,RidRoleOwner,InfrastructureRoleOwner
#
# I can even step the tree and get my forest root
#
$forest = $domain.Forest
$forest
#
# With our new found $forest object… what can do we do?
#
$forest | Get-Member
#
# WE can find all our GCs
#
$forest.FindAllGlobalCatalogs()
#
# We can look at the Forest Mode
#
$forest.ForestMode
#
# Look at the Forest FSMO
#
$forest | ft SchemaRoleOwner,NamingRoleOwner
#
# Even look at sites
$forest.Sites
#
# We can go on forever and ever. If you would like we can revisit this later.
#

The transition is complete (man that was NOT fun!)

I still have some minor adjustments, but all the content has been moved and the RSS links redirected. Please Let me know if you have any issues. I will be adding a contact form tomorrow.

More userAccountControl Flag Fun (Convert-ToUACFlag.ps1)

A question on the NG made me think about this. While I personally prefer the decimal that comes from userAccountControl, others may prefer to actually see the FLAGS that are set.

Here is the script I came up with. It will output and array by default, but -toString will output a “,” delimited string.

It has a great -help function with -verbose output that explains each UAC Flag

Convert-ToUACFlag.ps1

# Convert-ToUACFlag.ps1
Param([int]$uac,[switch]$ToString,[switch]$help,[switch]$verbose)
function HelpMe{
    Write-Host
    Write-Host " Convert-ToUACFlag.ps1:" -fore Green
    Write-Host "   Converts UAC from Decimal or Hex to User Account Control Flags (described verbose help)"
    Write-Host
    Write-Host " Parameters:" -fore Green
    Write-Host "   -UAC                   : Parameter User Account Control Value"
    Write-Host "   -toString              : [SWITCH]  Output to String instead of Array"
    Write-Host "   -Help                  : [SWITCH]  Displays This"
    Write-Host "   -Verbose               : [SWITCH]  Displays This and User Account Control Definitions"
    Write-Host
    Write-Host " Examples:" -fore Green
    Write-Host "   Convert to Flag getting back array" -fore White
    Write-Host "     .\Convert-ToUACFlag.ps1 69649" -fore Yellow
    Write-Host "   Convert to Flag getting back string" -fore White
    Write-Host "     .\Convert-ToUACFlag.ps1 69649 -toString" -fore Yellow
    Write-Host
    if($verbose)
    {
        Write-Host " User Account Control Flags and Definition" -fore Green
        Write-Host "  + SCRIPT" -fore Yellow
        Write-Host "    - The logon script will be run."
        Write-Host
        Write-Host "  + ACCOUNTDISABLE" -fore Yellow
        Write-Host "    - The user account is disabled."
        Write-Host
        Write-Host "  + HOMEDIR_REQUIRED" -fore Yellow
        Write-Host "    - The home folder is required."
        Write-Host
        Write-Host "  + PASSWD_NOTREQD" -fore Yellow
        Write-Host "    - No password is required."
        Write-Host
        Write-Host "  + PASSWD_CANT_CHANGE" -fore Yellow
        Write-Host "    - The user cannot change the password."
        Write-Host "    - This is a permission on the user’s object."
        Write-Host
        Write-Host "  + ENCRYPTED_TEXT_PASSWORD_ALLOWED" -fore Yellow
        Write-Host "    - The user can send an encrypted password."
        Write-Host
        Write-Host "  + TEMP_DUPLICATE_ACCOUNT" -fore Yellow
        Write-Host "    - This is an account for users whose primary account is in another domain."
        Write-Host "    - This account provides user access to this domain,"
        Write-Host "      but not to any domain that trusts this domain."
        Write-Host "    - This is sometimes referred to as a local user account."
        Write-Host
        Write-Host "  + NORMAL_ACCOUNT" -fore Yellow
        Write-Host "    - This is a default account type that represents a typical user."
        Write-Host
        Write-Host "  + INTERDOMAIN_TRUST_ACCOUNT" -fore Yellow
        Write-Host "    - This is a permit to trust an account for a system domain that trusts other domains."
        Write-Host
        Write-Host "  + WORKSTATION_TRUST_ACCOUNT" -fore Yellow
        Write-Host "    - This is a computer account for a computer that is running"
        Write-Host "    - Microsoft Windows NT 4.0 and above and is a member of this domain."
        Write-Host
        Write-Host "  + SERVER_TRUST_ACCOUNT" -fore Yellow
        Write-Host "    - This is a computer account for a domain controller that is a member of this domain."
        Write-Host
        Write-Host "  + DONT_EXPIRE_PASSWD" -fore Yellow
        Write-Host "    - Represents the password, which should never expire on the account."
        Write-Host
        Write-Host "  + MNS_LOGON_ACCOUNT" -fore Yellow
        Write-Host "    - This is an MNS logon account."
        Write-Host
        Write-Host "  + SMARTCARD_REQUIRED" -fore Yellow
        Write-Host "    - When this flag is set, it forces the user to log on by using a smart card."
        Write-Host
        Write-Host "  + TRUSTED_FOR_DELEGATION" -fore Yellow
        Write-Host "    - When this flag is set, the service account (the user or computer account)"
        Write-Host "      under which a service runs is trusted for Kerberos delegation."
        Write-Host "    - Any such service can impersonate a client requesting the service."
        Write-Host "    - To enable a service for Kerberos delegation, you must set this flag on the"
        Write-Host "      userAccountControl property of the service account."
        Write-Host
        Write-Host "  + NOT_DELEGATED" -fore Yellow
        Write-Host "    - When this flag is set, the security context of the user is not delegated to"
        Write-Host "      a service even if the service account is set as trusted for Kerberos delegation."
        Write-Host
        Write-Host "  + USE_DES_KEY_ONLY" -fore Yellow
        Write-Host "    - (Windows 2000/Windows Server 2003) Restrict this principal to use only"
        Write-Host "      Data Encryption Standard (DES) encryption types for keys."
        Write-Host
        Write-Host "  + DONT_REQUIRE_PREAUTH" -fore Yellow
        Write-Host "    - (Windows 2000/Windows Server 2003) This account does not require"
        Write-Host "      Kerberos pre+authentication for logging on."
        Write-Host
        Write-Host "  + PASSWORD_EXPIRED" -fore Yellow
        Write-Host "    - (Windows 2000/Windows Server 2003) The user’s password has expired."
        Write-Host
        Write-Host "  + TRUSTED_TO_AUTH_FOR_DELEGATION" -fore Yellow
        Write-Host "    - (Windows 2000/Windows Server 2003) The account is enabled for delegation."
        Write-Host "    - This is a security-sensitive setting."
        Write-Host "    - Accounts with this option enabled should be tightly controlled."
        Write-Host "    - This setting allows a service that runs under the account to assume a client’s"
        Write-Host "      identity and authenticate as that user to other remote servers on the network."
    }
    Write-Host
}

if(!$uac -or $help){HelpMe;Return}
$flags = @()
switch ($uac)
{
    {($uac -bor 0×0002) -eq $uac}    {$flags += "ACCOUNTDISABLE"}
    {($uac -bor 0×0008) -eq $uac}    {$flags += "HOMEDIR_REQUIRED"}
    {($uac -bor 0×0010) -eq $uac}    {$flags += "LOCKOUT"}
    {($uac -bor 0×0020) -eq $uac}    {$flags += "PASSWD_NOTREQD"}
    {($uac -bor 0×0040) -eq $uac}    {$flags += "PASSWD_CANT_CHANGE"}
    {($uac -bor 0×0080) -eq $uac}    {$flags += "ENCRYPTED_TEXT_PWD_ALLOWED"}
    {($uac -bor 0×0100) -eq $uac}    {$flags += "TEMP_DUPLICATE_ACCOUNT"}
    {($uac -bor 0×0200) -eq $uac}    {$flags += "NORMAL_ACCOUNT"}
    {($uac -bor 0×0800) -eq $uac}    {$flags += "INTERDOMAIN_TRUST_ACCOUNT"}
    {($uac -bor 0×1000) -eq $uac}    {$flags += "WORKSTATION_TRUST_ACCOUNT"}
    {($uac -bor 0×2000) -eq $uac}    {$flags += "SERVER_TRUST_ACCOUNT"}
    {($uac -bor 0×10000) -eq $uac}   {$flags += "DONT_EXPIRE_PASSWORD"}
    {($uac -bor 0×20000) -eq $uac}   {$flags += "MNS_LOGON_ACCOUNT"}
    {($uac -bor 0×40000) -eq $uac}   {$flags += "SMARTCARD_REQUIRED"}
    {($uac -bor 0×80000) -eq $uac}   {$flags += "TRUSTED_FOR_DELEGATION"}
    {($uac -bor 0×100000) -eq $uac}  {$flags += "NOT_DELEGATED"}
    {($uac -bor 0×200000) -eq $uac}  {$flags += "USE_DES_KEY_ONLY"}
    {($uac -bor 0×400000) -eq $uac}  {$flags += "DONT_REQ_PREAUTH"}
    {($uac -bor 0×800000) -eq $uac}  {$flags += "PASSWORD_EXPIRED"}
    {($uac -bor 0×1000000) -eq $uac} {$flags += "TRUSTED_TO_AUTH_FOR_DELEGATION"}
}
if($toString){$flags | %{if($mystring){$mystring += ",$_"}else{$mystring = $_}};$mystring}else{$flags}

Oisin the “obsessive programmer” sent me this as another option

param
([int]$value)
$flags = @("","ACCOUNTDISABLE","", "HOMEDIR_REQUIRED",
"LOCKOUT", "PASSWD_NOTREQD","PASSWD_CANT_CHANGE", "ENCRYPTED_TEXT_PWD_ALLOWED",
"TEMP_DUPLICATE_ACCOUNT", "NORMAL_ACCOUNT", "","INTERDOMAIN_TRUST_ACCOUNT", "WORKSTATION_TRUST_ACCOUNT",
"SERVER_TRUST_ACCOUNT", "", "", "DONT_EXPIRE_PASSWORD", "MNS_LOGON_ACCOUNT", "SMARTCARD_REQUIRED",
"TRUSTED_FOR_DELEGATION", "NOT_DELEGATED","USE_DES_KEY_ONLY", "DONT_REQ_PREAUTH",
"PASSWORD_EXPIRED", "TRUSTED_TO_AUTH_FOR_DELEGATION")
1..($flags.length) | ? {$value -band [math]::Pow(2,$_)} | % { $flags[$_] }

An Interactive Case for Powershell (Yet more Citrix Fun!)

I was recently in a discussion on Brian Madden Forums about listing Citrix Information and exporting to CSV. It seemed like a
perfect fit for Powershell so I converted the VBScripts to Powershell (of course taking an 85 line script to 3 lines. Convert is
hardly the correct term.)

Here is the Forum Topic

http://www.brianmadden.com/Forum/Topic/95285

Here is my Code. There were three scripts, so I made three as well.

# Apps by Server CTXApps_by_Server_w_Users
$MF = (New-Object -com MetaFrameCOM.MetaFrameFarm)
$MF.Initialize(1)
$MF.Servers | Select-Object ServerName -expand Applications | Select-Object ServerName,AppName,DistinguishedName,
       @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
       @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsByServer.Csv -noType
# Apps with Servers
$MF = New-Object -com MetaFrameCOM.MetaFrameFarm
$MF.Initialize(1)
$MF.Applications | Select-Object AppName,DistinguishedName,
      @{n="Servers";e={$_.Servers | foreach{$_.ServerName}}} | export-Csv C:\AppsWithServer.Csv -noType
# Apps with Servers and Users CTXApps_w_Servers_w_Users
$MF = New-Object -com MetaFrameCOM.MetaFrameFarm
$MF.Initialize(1)
$MF.Applications | Select-Object AppName,DistinguishedName,
      @{n="Servers";e={$_.Servers | foreach{$_.ServerName}}},
      @{n=‘Users’;e={$_.Users | %{$_.UserName}}},
      @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}} | export-Csv C:\AppsWithServerandUsers.Csv -noType

If you notice in my three scripts they all start with the same two lines. Effectively these are one liners that could be used
interactively. I think this does a great job of showing Citrix Admins how nicely Powershell will fit in to their daily lives.
Things that use to take 100s of lines of script writing can now be done interactively at a shell.

Get-Uptime (fun with Write-Host)

I am a BIG fan of Write-Host. Think of Write-Host like wscript.Echo (vbscript) or echo (cmd.exe,) on steroids. I know I know… you want to know what I think is the best feature and funny enough… its COLOR!!! Yes, COLOR, no more two color schemes. With Write-Host you can do all sorts of colors with foreground and background.

To celebrate, I wanted to write a post illustrating just how wonderful write-host can be and I thought it would be best to use a script that I wrote. This is a script to get the uptime of server.

Function Get-Uptime {
    Param([string]$server)
    Begin {
        function Uptime {
            param([string]$srv)
            $os = Get-WmiObject Win32_OperatingSystem -ComputerName $srv
            $uptime = $os.LastBootUpTime
            return $uptime
        }
        function ConvertDate {
            param([string]$date)
            $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)
            Write-Host "==> Uptime: " -NoNewLine
            Write-Host "$($uptime.days)" -NoNewLine -ForeGroundColor Green
            Write-Host " days, " -NoNewLine
            Write-Host "$($uptime.Hours)" -NoNewLine -ForeGroundColor Green
            Write-Host " hours, " -NoNewLine
            Write-Host "$($uptime.Minutes)" -NoNewLine -ForeGroundColor Green
            Write-Host " minutes, " -NoNewLine
            Write-Host "$($uptime.seconds)" -NoNewLine -ForeGroundColor Green
            Write-Host " seconds, "
        }
        Write-Host
        $process = @()
    }
    Process {
        if($_){
            if($_.ServerName ){
                $process += $_.ServerName
            }
            else{
                $process += $_
            }
        }
    }
    End {
        if($Server){$process += $server}
        foreach ($Server in $process){
            $result = uptime $server
            Write-Host "Uptime for Server [" -NoNewline
            Write-Host $server  -NoNewline -foregroundcolor Cyan
            Write-Host "]"
            ConvertDate $result
            Write-Host
        }
        Write-Host
    }
}

As you can see I use Write-Host numerous times so let me explain a couple of the lines so you can see what is happening.

Write-Host "==> Uptime: " -NoNewLine
Write-Host "$($uptime.days)" -NoNewLine -ForeGroundColor Green
Write-Host " days, " -NoNewLine
Write-Host "$($uptime.Hours)" -NoNewLine -ForeGroundColor Green
Write-Host " hours, " -NoNewLine
Write-Host "$($uptime.Minutes)" -NoNewLine -ForeGroundColor Green
Write-Host " minutes, " -NoNewLine
Write-Host "$($uptime.seconds)" -NoNewLine -ForeGroundColor Green
Write-Host " seconds, "

What I wanted was to have the output look like uptime.exe, but I wanted to add a little flare so I decided highlighting the numbers in a different color would make it easier to read.

There are two switches that I use here -NoNewLine and -ForeGroundColor. These are pretty self explanatory, but I will explain anyway.
-NoNewLine: This tells Write-Host not to add a New Line at the end of the string.

-ForeGroundColor: This tells you the color you want the text to display in (Black, DarkBlue, DarkGreen, DarkCyan, DarkRed, DarkMagenta, DarkYellow, Gray, DarkGray, Blue, Green, Cyan, Red, Magenta, Yellow, White.)

There are few other useful switches I didn’t use.
-BackGroundColor: Same available colors as ForeGroundColor, but this applies to the background color. So if you want the text to be Green on Black you would do
[code]Write-Host "Hello World" -ForeGroundColor Green -BackGroundColor Black[/code]

-Separator: This applies only if you pass an array of strings to write-host. This will prepend the “separator” to each string.

write-host ("hello","this","is","Separated","by","`"-`"") -Separator "-"

As you can see Write-Host is pretty useful, especially when you need your output to be nice and neat.

OH! Before I go… there is one thing to watch out for. Although for most people write-host behaves exactly as expected when you start working with PowerShell and start understanding Pipes its important to understand that Write-Host does NOT write to the pipeline. It writes to stdout. This is very important if you are trying to write stuff the pipe. To write to the pipe you need to use write-output.