Posts RSS Comments RSS 117 Posts and 170 Comments till now

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 0×40 (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.

  1. Param($User = $(throw ‘$User is Required’,[switch]$CheckBox)
  2. Write-Host
  3.  
  4. $Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"","(&(objectcategory=User)(sAMAccountName=$user))")
  5. $MyUser = $Searcher.FindOne().GetDirectoryEntry()
  6.  
  7. if(!$?){" !! Failed to Get User !!";Return}
  8.  
  9. if($CheckBox)
  10. {
  11.     Write-Host " - Checking Box for User [$($MyUser.distinguishedName)]"
  12.     $self = [System.Security.Principal.SecurityIdentifier]‘S-1-5-10′
  13.     $ExtendedRight = [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight
  14.     $deny = [System.Security.AccessControl.AccessControlType]::Deny
  15.     $selfDeny = new-object System.DirectoryServices.ActiveDirectoryAccessRule($self,$ExtendedRight,$deny,‘ab721a53-1e2f-11d0-9819-00aa0040529b’)
  16.     $MyUser.psbase.get_ObjectSecurity().AddAccessRule($selfDeny)
  17.     $MyUser.psbase.CommitChanges()
  18. }
  19. else
  20. {
  21.     Write-Host " - Removing Check Box for User [$($MyUser.distinguishedName)]"
  22.     $ACL = $MyUser.psbase.get_ObjectSecurity().GetAccessRules($true,$false, [System.Security.Principal.NTAccount])
  23.     $ACEs = $ACL | ?{($_.ObjectType -eq ‘ab721a53-1e2f-11d0-9819-00aa0040529b’) -and ($_.AccessControlType -eq ‘Deny’)}
  24.     foreach($ACE in $ACEs){if($ACE){[void]$MyUser.psbase.get_ObjectSecurity().RemoveAccessRule($ACE)}}
  25.     $MyUser.psbase.CommitChanges()
  26. }
  27.  
  28. Write-Host

More AD CMDLets from SDMSoftware

It includes snapins to get and re-animate tombstones

Darren discusses this on his blog: Powershell Hits The Morgue

Intro to VMWare VI Toolkit for Windows (Playing Around Series)

I know there are a good bit of people out there that are unable install and play with the new VMWare toolkit. So in this post I though I would record some of my playing around with Get-VM :)

I show Get-VM, Get-HardDisks, Get-CDDrive, Get-NetworkAdapter, and Get-FloppyDrive. I recommend pausing and reading the video as you go.

Note: This is a fast run through. I recommend pausing and reading the Comments.

BEST VIEWED FULL SCREEN
Get the Flash Player to see this player.

Some shout outs:
To Hal from PowerScripting.Net for use of his Lab for recordings. Here is his blog: Hals Blog
To Camtasia for incredible product:
Click Here
To VMWare for some Great CMDLets: Click Here

Demo File

  1. Get-VC home.halr9000.com
  2. # Lets look at the all the commands we have.
  3. Get-Vicommand | more
  4. # Now lets look at Get-VM
  5. Get-VM
  6. # What properties do we have for the VMs
  7. Get-VM | Get-Member -type property
  8. # Lets look at the Hard Disks
  9. Get-VM | Get-HardDisk
  10. # Here are the properties for a HardDisk
  11. Get-VM | Get-HardDisk | Get-Member -type Property
  12. # Lets look at VMs with over 2gb of disk
  13. Get-VM | Get-HardDisk | ?{$_.CapacityKB*1kb -gt 2gb}
  14. # What about 4gb
  15. Get-VM | Get-HardDisk | ?{$_.CapacityKB*1kb -gt 4gb}
  16. # Now lets look at the CD Drives
  17. Get-VM | Get-CDDrive
  18. # What about the properties available
  19. Get-VM | Get-CDDrive | get-member -type property
  20. # Lets make sure none of the CD’s have ISO’s attached
  21. Get-VM | Get-CDDrive | ?{$_.ISOPath}
  22. # How bout NICs
  23. Get-VM | Get-NetworkAdapter
  24. # what do they have to offer?
  25. Get-VM | Get-NetworkAdapter | get-member -type property
  26. # Lets look at all the NICs, but only the Mac and NetworkName
  27. Get-VM | Get-NetworkAdapter | select MacAddress,NetworkName
  28. # Finally, Lets take a quick peak at floppies
  29. Get-VM | Get-FloppyDrive

Citrix Script Repo (aka Exchange)

I was recently directed to a new website (for me at least) that contains a repository of Citrix related scripts. There are some pretty useful scripts posted (and I started adding some Powershell ones.) You should check it out

Script Exchange
http://community.citrix.com/display/cdn/Script+Exchange

I would also recommend RSS’ing Vishal blog (Dev at Citrix.) He has a passion for Powershell and I hope to see some awesome things from him in the future.

Vishal Ganeriwala’s Blog
http://community.citrix.com/blogs/citrite/vishalg/

Set-CitrixPSVersion.ps1 (Citrix Top 10)

I am constantly looking for content for my blog and tasks that can be automated. I believe this helps me learn and helps other with their needs.

In this search I was directed to convert/write Powershell examples for scripts located here http://community.citrix.com/display/cdn/Script+Exchange

Here is the first of those scripts.

Name: Set-CitrixPSVersion.ps1
Purpose: Sets PS Version on a Server, List of Servers, or all Servers in the Farm

  1. # Set-CitrixPSVersion.ps1
  2. # Brandon Shell [MVP]
  3. # www.bsonposh.com
  4. # Sets PS Version on a Server, List of Servers, or all Servers in the Farm
  5. Param($file,$server,$PSVer,[switch]$help,[switch]$all,[switch]$whatif,[switch]$show,[switch]$verbose)
  6. function HelpMe{
  7.     Write-Host
  8.     Write-Host " Set-CitrixPSVersion.ps1:" -fore Green
  9.     Write-Host "   Sets PS Version on a Server, List of Servers, or all Servers in the Farm"
  10.     Write-Host
  11.     Write-Host " Parameters:" -fore Green
  12.     Write-Host "   -File <fileName>       : Optional. Name of the File of Servers"
  13.     Write-Host "   -Server <serverName>   : Optional. Name of the Server to Change"
  14.     Write-Host "   -Verbose               : Optional. Enables Verbose Output"
  15.     Write-Host "   -All                   : Optional. Sets Version on all Servers [Requires -Server]"
  16.     Write-Host "   -Show                  : Optional. Displays the Version for Server(s)"
  17.     Write-Host "   -Help                  : Optional. Displays This"
  18.     Write-Host "   -Whatif                : Optional. Will not Commit Info just Display what would change"
  19.     Write-Host
  20.     Write-Host " Examples:" -fore Green
  21.     Write-Host "   Set PS Version on Server1 to STD" -fore White
  22.     Write-Host "     .\Set-CitrixPSVersion.ps1 -Server Server1 -psver STD " -fore Yellow
  23.     Write-Host
  24.     Write-Host "   Set PS Version on Servers in a File" -fore White
  25.     Write-Host "     .\Set-CitrixPSVersion.ps1 -file c:\Mylist.txt -psver STD " -fore Yellow
  26.     Write-Host
  27.     Write-Host "   Get PS Version from ALL server" -fore White
  28.     Write-Host "     .\Set-CitrixPSVersion.ps1 -all -Server myzdcserver" -fore Yellow
  29.     Write-Host
  30.     Write-Host " Product Edition Options" -fore Green
  31.     Write-Host "   STD = Citrix Presentation Server Standard Edition" -fore White
  32.     Write-Host "   ADV = Citrix Presentation Server Advanced Edition" -fore White
  33.     Write-Host "   ENT = Citrix Presentation Server Enterprise Edition" -fore White
  34.     Write-Host "   PLT = Citrix Presentation Server Platinum Edition" -fore White
  35.     Write-Host
  36. }
  37. function Ping-Server {
  38.     Param($srv)
  39.     $pingresult = Get-WmiObject win32_pingstatus -f "address=’$srv’"
  40.     if($pingresult.statuscode -eq 0) {$true} else {$false}
  41. }
  42. function Set-PSVer{
  43.     Param($srv)
  44.     Write-Verbose "  Getting Citrix Server Object"
  45.     $type = [System.Type]::GetTypeFromProgID("MetaframeCOM.MetaframeServer",$srv)
  46.     $mfsrv = [system.Activator]::CreateInstance($type)
  47.     $mfsrv.Initialize(6,$srv)
  48.     if(!$?){Write-Host "   - Server [$srv] threw and Error" -fore red;return}
  49.  
  50.     if($show) # Check for $Show and will display only
  51.     {
  52.         Write-Host "   - PS Version for [$srv]: $($mfsrv.WinServerObject.mpsedition)"
  53.         return
  54.     }
  55.     else
  56.     {
  57.         if($whatif) # Check for $Whatif
  58.         {
  59.             Write-Host "  What if: Performing operation `"Set PS Version [$PSVer]`" on Target `"$srv`"."
  60.         }
  61.         else
  62.         {
  63.             Write-Verbose "   - Setting PSVer to [$PSVer] on [$srv]"
  64.             $mfsrv.WinServerObject.mpsedition = $PSVer
  65.             Write-Host "   - PS Version for [$srv]: $($mfsrv.WinServerObject.mpsedition)"
  66.         }
  67.     }
  68. }
  69. function Set-PSALL{
  70.     $mfarm = new-Object -com "MetaframeCOM.MetaframeFarm"
  71.     $mfarm.Initialize(1)
  72.     foreach($mfsrv in $mfarm.Servers)
  73.     {
  74.         if($show){Write-Host "   - PS Version for [$($mfsrv.ServerName)]: $($mfsrv.WinServerObject.MPSEdition)";continue}
  75.         if($whatif){Write-Host "  What if: Performing operation `"Set PS Version [$PSVer]`" on Target `"$($mfsrv.ServerName)`"."}
  76.         else
  77.         {
  78.             Write-Verbose "   - Setting PSVer to [$PSVer] on [$($mfsrv.ServerName)]"
  79.             $mfsrv.WinServerObject.mpsedition = $PSVer
  80.             Write-Host "   - PS Version for [$($mfsrv.ServerName)]: $($mfsrv.WinServerObject.MPSEdition)"
  81.         }
  82.     }
  83. }
  84.  
  85. # Script Setup. Checking Parameters
  86. Write-Host
  87.  
  88. ## Checing Verbose flag
  89. if($verbose){$verbosepreference = "Continue"}else{$erroractionpreference = "SilentlyContinue"}
  90.  
  91. ## Verifying that File/Server was passed. If not or -help I Call HelpMe and close.
  92. if(!$file -and !$server -and !$all -or $help){HelpMe;Return}
  93.  
  94. ## Verify Valid Edition was Passed
  95. if(!$show -and ($PSVer -notmatch "STD|ADV|ENT|PLT"))
  96. {
  97.     Write-Host " PS Edition [$PSVER] is NOT Valid. Please use STD, ADV, ENT, or PLT" -fore RED
  98.     Write-Host
  99.     Return
  100. }
  101.  
  102. # If $Server and we can ping it we run Set-PSVer against Server
  103. if($server -and (ping-server $server))
  104. {
  105.     Set-PSVer $server
  106.     Write-host
  107. }
  108.  
  109. # Check for -File and Verify the file is valid
  110. if($File -and (test-Path $file))
  111. {
  112.     Write-Verbose " - Processing File [$file]"
  113.     # Process each Server checking for blanks
  114.     foreach($Server in (get-Content $file | where{$_ -match "^\S"}))
  115.     {
  116.         Write-Host " + Processing Server [$Server]"
  117.         if(ping-Server $server){Set-PSVer $Server}else{Write-Host "   - Ping Failed to [$Server]" -fore Red}
  118.         Write-host
  119.     }
  120. }
  121.  
  122. if($all){Set-PSALL}
  123.  
  124. Write-Host

Great Info for Profiles

This is a blog of a friend of mine that works at MS. He wrote UPHClean and is incredibly smart guy.

You definately should RSS his Blog: http://blogs.technet.com/uphclean/

Test-Port (kinda like portqry without verbose output)

We had a little dicussion on www.powershelllive.com forums about the most efficient way to Test a machine before trying a WMI query against it (as it has a log timeout.) My first suggestion was to use a ping (WMI style) but Jeff from http://blog.sapien.com brought up a valid point… what if ICMP is NOT Allowed…

Enter Test-Port. This nifty little script uses the TCPClient Class to test connectivity. Stay tuned as I am planning some mods.

Test-Port
- Takes parameter $srv for Server Name
- Takes Parameter for Port, Defaults to 135 for RPC mapper.
- Takes Timeout.. defaults to 3000 (miliseconds)
- If it cannot connect within timeout… Returns $false
- If it gets exception connecting to port… Returns $false
- If it connects… Returns $True

  1. function Test-Port{
  2.     Param([string]$srv,$port=135,$timeout=3000,[switch]$verbose)
  3.     $ErrorActionPreference = "SilentlyContinue"
  4.     $tcpclient = new-Object system.Net.Sockets.TcpClient
  5.     $iar = $tcpclient.BeginConnect($srv,$port,$null,$null)
  6.     $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
  7.     if(!$wait)
  8.     {
  9.         $tcpclient.Close()
  10.         if($verbose){Write-Host "Connection Timeout"}
  11.         Return $false
  12.     }
  13.     else
  14.     {
  15.         $error.Clear()
  16.         $tcpclient.EndConnect($iar) | out-Null
  17.         if($error[0]){if($verbose){write-host $error[0]};$failed = $true}
  18.         $tcpclient.Close()
  19.     }
  20.     if($failed){return $false}else{return $true}
  21. }

Using Aliases in Scripts or in your Examples.

Richard has a post on his blog about this The Over Use of Aliases?

I dont like duplicating other peoples post, but I think this one is worth it.

If you are an experienced Powershell person and you provide help or scripts to people. PLEASE do not use aliases. It makes it very hard for people. I am guilty myself. I mean gc % ? gm are SO tempting, but it really doesn’t help the person learn (it may even hinder.) IMO, using aliases can confuse newcomers… remember to them when you type gc… they think it is gc.exe not get-content. Peoples first impression is key… lets not make it a confusing one :)
Basically, I think aliases are best use for interactive use, but should be avoided in scripts or sample code.

p.s. COMMENT… COMMENT… COMMENT… Comments in code help a ton for people learning.

UPDATE:
Jeffrey brought up a good point that I over looked. Numerous Powershell IDE’s like PrimalScript and Powershell Analzyer automatically expand built in aliases. I find this very useful. I will be writing another post about IDE’s and why I think they are VERY important.