Posts RSS Comments RSS 253 Posts and 411 Comments till now

Some AD Functions for DCs and name conversion.

Below are some functions I have written or used recently that I don’t believe I shared before

Get-DC: Gets a DirectoryServices.ActiveDirectory.DomainController object by Name or Domain. If nothing is passed it gets a DC from the current domain

Get-DCConnectionObject: Gets the connection objects for the given DC. Default is all DCs

ConvertTo-Sid: Converts Name to SID.

ConvertTo-Name: Converts Sid to Name.

################################################################################
function Get-DC
{
    Param($Name,$Domain)
   
    if($Name)
    {
        $Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$Name)
        [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($Context)
    }
    if($Domain)
    {
        $Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain",$Domain)
        [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context)
    }
    if(!$Name -and !$Domain)
    {
        $DCName = ([adsi]"LDAP://rootDSE").dnsHostname.ToString()
        $Context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("DirectoryServer",$DCName)
        [System.DirectoryServices.ActiveDirectory.DomainController]::GetDomainController($Context)
    }
}
################################################################################
function Get-DCConnectionObject
{
    Param($name = ".*")
    $Myforest = [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
    $MyDCs = $Myforest.Domains | foreach-object{$_.DomainControllers} | ?{$_.name -match $name}
    $MyDCs | %{$_.InboundConnections}
}
################################################################################
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
}

Regular Expression Info and Tips for Powershell

One of the most useful tools a scripter has at their disposal is Regular Expressions. The problem is that regular expressions can seem like Greek and for most folks learning them can seem an impossible task. I hope this blog post provides those people some hope as well as some tools they can use to tackle this mountain one foot at a time.

NOTE: I would like to make this blog entry as dynamic as possible, so if you have your own tips… let me know and I will add them.


Everyone is different, but this is how I went about learning Regular Expressions

  • Phase one: Learn EXACTLY what a regular expression is and what it can and can NOT do.
  • Phase two: Learn the syntax. I printed out the RegEx Cheet Sheet and use it daily.
  • Phase three: Find a purpose. I find if I actually have a problem I spend more energy on learning.
  • Phase four: Create a simple Regular Expression.
  • Phase five: Build on your Regular Expression.
  • Phase six: Trouble shooting the Regular Expression.

Useful Tips:

  • First things first… Click here and watch the vids
  • Keep a cheat sheet close at hand!
  • Take them in small bites. I hope to blog in detail about this later, but lets just say string concatenation is your friend
  • Keep it simple!
  • $matches is your friend! $matches is an object that Powershell creates when you make a RegEx call (like -match.)
  • Google It. Chances are there is Regex already out there for what you need.
  • Using Lables can save you time by extracting certain data and making it a property on $matches.
  • RichardP from IRC says “Dont eat yellow snow?” Apparently he has experience in this area.

Online Vids:
This is GREAT stuff provided by Shay (aka scriptfanatic)

Good Blog Entries:
MoW at it again.

References:
RegEx Cheet Sheet (my Favorite)
Regular-Expressions.info
Regular Expression Library

Online Regex Checkers:
RegEx Tester (my Favorite)
Regular Expression Library Tester

Books:
Mastering Regular Expressions (my Favorite)
Regular Expression Recipes for Windows Developers

Tools:
Expresso
RegEx Buddy

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

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"
    }
}

Setting lDAPAdminLimits via Powershell

I was having a conversation with a friend the other day and he brought up a question about updating the AD property lDAPAdminLimits.

The Problem
The property is stored as an array of string values (at least as far as Powershell is concerned.) The initial reaction was do try something like this $queryPolicies.lDAPAdminLimits.MaxNotificationPerConn = 30, but this assumed that MaxNotficationPerConn was a property of lDAPAdminLimits and not the actual value (or at least part of the value.)

The Solution
Use the ADSI method PutEX to modify the value. PutEx uses ADS_PROPERTY_OPERATION_ENUM to make selective changes to an existing property. In the script below, we add the new value using the Update operation and then use the Delete operation to remove the old value.

Some Examples of Use

D:\Scripts\Set-ldapAdminPolicy.ps1 MaxNotificationPerConn 45
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxQueryDuration 360
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxPageSize 500
D:\Scripts\Set-ldapAdminPolicy.ps1 MaxPoolThreads 8

Here is a link on how to view/set via NTDSUtil.exe
How to view and set LDAP policy in Active Directory by using Ntdsutil.exe

The Code

Param($policy=$(throw ‘$policy is required’),$count=30)
$rootDSE = [ADSI]"LDAP://rootDSE"
$config = $rootDSE.configurationNamingContext
$queryPolicies = [adsi]"LDAP://CN=Default Query Policy,CN=Query-Policies,cn=Directory Service,cn=Windows NT,CN=Services,$config"
$oldvalue = $queryPolicies.lDAPAdminLimits | ?{$_ -match $policy}
$queryPolicies.PutEx(3,"lDAPAdminLimits",@("$policy=$count"))
$queryPolicies.Setinfo()
$queryPolicies.PutEx(4,"lDAPAdminLimits",@("$oldvalue"))
$queryPolicies.Setinfo()

Multiple Paths to the same End (Citrix)

Today I was asked this question

“Is there a more efficient way to list each published app and list out servers that are associated with it? In hope to give me an output that will display the ‘AppName’ objects once and display all the servers.”

He was referring to script I have posted somewhere (/shrug.)

I knew exactly what he wanted and thought “HEY! This would be a good time to blog about MFCom object Nesting.”

What do I mean by “MFCom object Nesting?” I simply mean that MFCom object model is nested in such a way that you can attain the same information from various code paths. For example, Farm objects contain Application objects which contain Server objects, but Farm objects also have Server objects which contain Application objects.

Farm -> Applications -> Servers
Or
Farm -> Servers -> Applications

Why is this important or why would Citrix do this? IMO, it is to provide a shorter code path for the information you want.

Here are some examples

In the case of the question I received, what he had required getting all the applications from each server and returning the information like this:

$mfarm.Servers | Select ServerName -expandproperty Applications | Select ServerName,
                                                                          AppName,
                                                                          DistinguishedName,
                                                                          @{n="Groups";e={$_.Groups | %{$_.GroupName}}}

What he wanted to get was all the applications and output the information directly like:

$mfarm.Applications | %{$_.LoadData(1);$_} | Select-Object AppName,
                                                           DistinguishedName,
                                                           @{n=‘Groups’;e={$_.Groups | %{$_.GroupName}}},
                                                           @{n=‘Servers’;e={$_.Servers | %{$_.ServerName}}}

 
Perhaps a more direct example would be collecting Sessions. If I want all the Sessions for a Server I could do one of the following:

$farm.Sessions | ?{$_.ServerNamematch "Server1"}

this works, but has to touch all my sessions… a better way would be like:

Get-CitrixServer  "Server1" | %{$_.Sessions}

 
More Info on LoadData()
If you are not familiar with the LoadData() method on the Application object it is critical that you get familiar, and get familiar quick.

LoadData() was introduced in MPS 4.0 (I believe) to allow the farm (or directly instantiated Application objects) to be returned with just a small bit of information. This allows you to quickly get basic application information without having to collect the entire data set, saving both time and network traffic. The problem is that most people do not know this and it can get quiet messy.

Why is it messy? If you get an application and set a property (like adding a user) and then commit the data back to the server using the SaveData() method, what do you think will happen? One could expect it would only update that which has change, but one would be wrong. When you call SaveData() it actually commits whatever is in local memory to the farm (yup, you got it… HOSED!) You just committed an effectively empty application and set it back to the Farm wiping out any existing information.

Moral of the story? USE LoadData().

Using Conditional Operators with “-not(match|like)”

I recently answered a post on PoshComm about the use of -or and -and. There was a little confusion on the expected results as this gets especially confusing when using ‘-not’, ‘-notmatch’, or ‘-notlike’. It is effectively a difference between “Include ALL but x” vs “Exclude ALL but x.”

The key to understanding how ‘-or’ and ‘-and’ will work is understanding that everything evaluates to $true or $false and this value is returned for EVERY condition check independantly. Working from the inside (left to right) to the outside (left to right.)

Here is an example of outter and inner conditions.

# Outter Condition 1 (-or’s the return of "inner Condition 1" and the return of "inner Condition 2")
where(
   # inner Condition 1 (Processed First)
   ($description -notmatch "TestOne")
   -or
   # inner Condition 2 (Processed Second)
   ($description -notmatch "AnotherTest")
)

Below is a some working code that will illustrate the point.
The goal is to find all the descriptions that do not match ‘TestOne’ or ‘AnotherTest’.

# Setup a Variable to Test against
$myDescriptions = @("My Description has an ‘TestOne’ in it")
1..2 | %{$myDescriptions += "Really descriptive ‘s$_’ description"}
$myDescriptions += "My Description has an ‘AnotherTest’ in it"
foreach($description in $myDescriptions)
{
    $matchTestOne           = $description  -notmatch "TestOne"
    $matchAnotherTest       = $description  -notmatch "AnotherTest"
    $UsingOr                = ($description -notmatch "TestOne") -or ($description -notmatch "AnotherTest")
    $UsingNotMatchCorrectly = $description  -notmatch "(TestOne|AnotherTest)"
    "Value:                    $description"
    "Match One:                $matchTestOne"
    "Match Two:                $matchAnotherTest"
    "The Or:                   $UsingOr"  
    "Using NotMatch Correctly: $UsingNotMatchCorrectly"
    Write-Host
}

Here is the output from the above code. Notice that $UsingOr does not return as the one might expected. This is because every description will pass at least one of the conditions and therefore the entire condition will return $true.

Value:                    My Description has an ‘TestOne’ in it
Match One:                False
Match Two:                True
The Or:                   True
Using NotMatch Correctly: False

Value:                    Really descriptive ‘s1’ description
Match One:                True
Match Two:                True
The Or:                   True
Using NotMatch Correctly: True

Value:                    Really descriptive ‘s2’ description
Match One:                True
Match Two:                True
The Or:                   True
Using NotMatch Correctly: True

Value:                    My Description has an ‘AnotherTest’ in it
Match One:                True
Match Two:                False
The Or:                   True
Using NotMatch Correctly: False

Get-CitrixApplication.ps1 (Citrix Top 10)

This script returns Citrix Application Objects.
– With no -AppName passed it will return All application Objects
– With -AppName it will return all apps that match (regex.)

# Get-CitrixApplication.ps1
# Brandon Shell [MVP]
# www.bsonposh.com
# Returns Citrix Application Objects for AppName passed or RegEx
Param($AppName=".*",$server=$env:ComputerName)
$type = [system.Type]::GetTypeFromProgID("MetaframeCOM.MetaFrameFarm",$server)
$farm = [system.Activator]::CreateInstance($type)
$farm.Initialize(1)
$farm.Applications | ?{($_.AppName -match $AppName) -or ($_.BrowserName -match $AppName)}

SpecOps and Group Policies… What a match!

Special Operations Software has created an Incredible marriage of Powershell and Group Policy. Please take some time to watch these Demos. AWESOME!

Specops Command done by Darren Mar-Elia:

Specops Deploy done by Derek Melber:

Kill-UserProcess (my first whatif Script)

There was a post on the forums about Killing all the processes for a user so I decided to write a script for it… sounded like something I could use. I thought about using get-process or [System.Diagnostics.Process] for remoting, but instead I went with WMI.

Below is the script and here are some example usages

Get User Processes on Server
PS> kill-userProcess -server Serverx -user jloser
Get User Processes on Server and Kill using -whatif
PS> kill-userProcess -server Serverx -user jloser -kill -whatif
Get User Processes on Server and kill them
PS> kill-userProcess -server Serverx -user jloser -kill
Get User Process on Server Kill/Whatif
PS> kill-userProcess -server Serverx -user jloser -process explorer.exe -kill -whatif
Get User Process on Server just kill
PS> kill-userProcess -server Serverx -user jloser -process explorer.exe -kill

[code]
function Kill-UserProcess{
param([string]$server,[string]$user,[string]$process,[switch]$Kill,[switch]$whatif)
if($server){$processes = Get-WmiObject Win32\_Process -ComputerName $server}
else{$processes = Get-WmiObject Win32\_Process}
if($user){
if($kill){if(!$process){Write-Host “Killing all Processes for User [$user]”}}
foreach($p in $processes){
if($p.GetOwner().user -match “$user”){
if($process){
if($p.Name -match “$process”){
if($kill){
Write-Host “Killing Process [$($p.Name)] for User [$user]”
if($whatif){
write-Host “What if: Performing operation “”kill”” on Target “”$($p.Name)””.”
}
}
else{
Write-Host “Killing Process $($p.Name)”
$p.Terminate() | out-null
}
return $true
}
}
if($kill){
if($whatif){
write-Host “What if: Performing operation “”kill”” on Target “”$($p.Name)””.”
}
else{
Write-Host “Killing Process $($p.Name)”
$p.Terminate() | out-Null
}
}
else{
Write-Host “$p.Name”
}
}
}
}
}
[/code]