Categories

Monthly Archives: January 2012

Reading direct reports

As stated last time the direct reports is a backlink created from the users who have this particular user as a manager

Displaying this attribute is straight forward

if (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}            
            
$ou = "OU=England,DC=Manticore,DC=org"            
$manager = "CN=HORNBLOWER Horatio,$ou"            
            
"`nMicrosoft"            
Get-ADUser -Identity $manager -Properties * |             
select -ExpandProperty directReports            
            
            
"`nAD provider"            
Get-ItemProperty -Path AD:\$manager  -Name directReports |             
select -ExpandProperty directReports            
             
"`nQuest"            
Get-QADUser -Identity $manager -IncludeAllProperties |             
select -ExpandProperty directReports            
            
            
"`nScript"            
$user = [adsi]"LDAP://$manager"            
$user |             
select -ExpandProperty directReports


No real surprises – get the user and display the property. Its a collection so using –ExpandProperty is the easiest way to display

User organisation details

The organization tab on the user properties can hold a number of items – job title, department, company, manager & direct reports

The first four we can set directly. The direct reports attribute is a backlink meaning it is filled with the distinguished names of the users who have  a particular user set as their manager.

The properties we can set are managed like this

if (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}            
            
$ou = "OU=England,DC=Manticore,DC=org"            
            
$dept = "Secret Squirrel stuff"            
$comp = "Hush Hush Co Ltd"            
$manager = "CN=HORNBLOWER Horatio,$ou"            
            
"`nMicrosoft"            
$name = "UserA"            
$title = "VP for stuff"            
Get-ADUser -Identity $name |            
Set-ADUser -Title $title -Department $dept -Company $comp -Manager $manager            
            
"`nAD provider"            
$name = "UserB"            
$title = "VP for something or other"            
$dn = "cn=$name,$ou"            
Set-ItemProperty -Path AD:\$dn  -Name title      -Value $title   -Force            
Set-ItemProperty -Path AD:\$dn  -Name department -Value $dept    -Force            
Set-ItemProperty -Path AD:\$dn  -Name company    -Value $comp    -Force            
Set-ItemProperty -Path AD:\$dn  -Name manager    -Value $manager -Force            
             
"`nQuest"            
$name = "UserC"            
$title = "VP for things"            
Get-QADUser -Identity $name |            
Set-QADUser -Title $title -Department $dept -Company $comp -Manager $manager            
            
"`nScript"            
$name = "UserD"            
$title = "VP for other things"            
$dn = "cn=$name,$ou"            
$user = [adsi]"LDAP://$dn"            
$user.title = $title            
$user.department = $dept            
$user.company = $comp            
$user.manager = $manager            
$user.SetInfo()


 



The cmdlets have parameters for each attribute while the provider and script have to work a bit harder

Naming Contexts

Continuing our quick look at The ActiveDirectory name space lets have a look at the MSAD_NamingContext  class

Get-WmiObject -Namespace root\MicrosoftActiveDirectory -Class MSAD_NamingContext |

Format-Table DistinguishedName, IsFullReplica –AutoSize

 

DistinguishedName                              IsFullReplica
-----------------                              -------------
DC=DomainDnsZones,DC=Manticore,DC=org                   True
DC=ForestDnsZones,DC=Manticore,DC=org                   True
CN=Schema,CN=Configuration,DC=Manticore,DC=org          True
CN=Configuration,DC=Manticore,DC=org                    True
DC=Manticore,DC=org                                     True

 

This is equivalent to the information you see in the root of the AD provider

PS> Get-ChildItem -Path AD:\

Name                 ObjectClass          DistinguishedName
----                 -----------          -----------------
Manticore            domainDNS            DC=Manticore,DC=org
Configuration        configuration        CN=Configuration,DC=Manticore,DC=org
Schema               dMD                  CN=Schema,CN=Configuration,DC=Manticore,DC=org
ForestDnsZones       domainDNS            DC=ForestDnsZones,DC=Manticore,DC=org
DomainDnsZones       domainDNS            DC=DomainDnsZones,DC=Manticore,DC=org

Active Directory and WMI

A lot of the Active Directory related functionality has been removed from WMI but  there is a little bit left in the root\MicrosoftActiveDirectory namespace.

This is on a Windows 2008 R2 domain controller – I don’t know if this is available on down level versions of Windows.

Get-WmiObject -Namespace root\MicrosoftActiveDirectory -List | where {$_.Name -notlike "__*"}

 

ReplicationProvider1
MSAD_ReplPendingOp
Microsoft_TrustProvider
Microsoft_DomainTrustStatus
Microsoft_LocalDomainInfo
MSAD_NamingContext
MSAD_ReplCursor
MSAD_DomainController
MSAD_ReplNeighbor

The mixture of naming conventions doesn’t help but lets start looking at some domain information

Get-WmiObject -Namespace root\MicrosoftActiveDirectory -Class Microsoft_LocalDomainInfo

 

The following properties of interest are returned

DCname           : SERVER02
DNSname          : Manticore.org
FlatName         : MANTICORE
SID              : S-1-5-21-3881460461-1879668979-35955009
TreeName         : Manticore.org

 

We can also get a quick replication test

 

Get-WmiObject -Namespace root\MicrosoftActiveDirectory -Class MSAD_DomainController |
select CommonName, DistinguishedName, IsAdvertisingToLocator, IsGC, IsNextRIDPoolAvailable,
IsRegisteredInDNS, IsSysVolReady, NTDsaGUID, PercentOfRIDsLeft, SiteName,
@{N="OldestQueuedAddition"; E={$_.ConvertToDateTime($_.TimeOfOldestReplAdd)} },
@{N="OldestQueuedDeletion"; E={$_.ConvertToDateTime($_.TimeOfOldestReplDel)} },
@{N="OldestQueuedModification"; E={$_.ConvertToDateTime($_.TimeOfOldestReplMod)} },
@{N="OldestQueuedReplicationSync"; E={$_.ConvertToDateTime($_.TimeOfOldestReplSync)} },
@{N="OldestQueuedReplicationUpdate"; E={$_.ConvertToDateTime($_.TimeOfOldestReplUpdRefs)} }

 

CommonName                    : SERVER02
DistinguishedName             : CN=NTDS Settings,CN=SERVER02,CN=Servers,CN=Site1,CN=Sites,CN=Configuration,DC=Manticore,DC=org
IsAdvertisingToLocator        : True
IsGC                          : True
IsNextRIDPoolAvailable        : False
IsRegisteredInDNS             : True
IsSysVolReady                 : True
NTDsaGUID                     : baba1150-8a6a-41ac-9889-4b69268d3f7c
PercentOfRIDsLeft             : 91
SiteName                      : Site1
OldestQueuedAddition          : 01/01/1601 00:00:00
OldestQueuedDeletion          : 01/01/1601 00:00:00
OldestQueuedModification      : 01/01/1601 00:00:00
OldestQueuedReplicationSync   : 01/01/1601 00:00:00
OldestQueuedReplicationUpdate : 01/01/1601 00:00:00

 

The 1601 dates mean nothing is queued

Setting user address information

In AD Users & Computers the user’s properties dialog has a tab for setting address information

 

if (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}            
            
$ou = "OU=England,DC=Manticore,DC=org"            
$street = @"
123 Somewhere Place, 
A big suburb
"@             
            
$PObox = "PO 456"            
$city = "Salisbury"            
$state = "Wiltshire"            
$zip = "SY27 5QW"            
$country = "GB"            
            
"`nMicrosoft"            
$name = "UserA"            
Get-ADUser -Identity $name |            
Set-ADUser -StreetAddress $street -POBox $PObox -City $city -State $state -PostalCode $zip -Country $country            
            
"`nAD provider"            
$name = "UserB"            
$dn = "cn=$name,$ou"            
Set-ItemProperty -Path AD:\$dn  -Name streetAddress -Value $street  -Force            
Set-ItemProperty -Path AD:\$dn  -Name postOfficeBox -Value $POBox   -Force            
Set-ItemProperty -Path AD:\$dn  -Name l             -Value $city    -Force            
Set-ItemProperty -Path AD:\$dn  -Name st            -Value $state   -Force            
Set-ItemProperty -Path AD:\$dn  -Name postalCode    -Value $zip     -Force            
Set-ItemProperty -Path AD:\$dn  -Name c             -Value $country -Force            
             
"`nQuest"            
$name = "UserC"            
Get-QADUser -Identity $name |            
Set-QADUser -StreetAddress $street -PostOfficeBox $PObox -City $city -StateOrProvince $state -PostalCode $zip -ObjectAttributes @{c=$country}            
            
"`nScript"            
$name = "UserD"            
$dn = "cn=$name,$ou"            
$user = [adsi]"LDAP://$dn"            
$user.streetAddress = $street            
$user.postOfficeBox = $PObox            
$user.l = $city            
$user.st = $state            
$user.postalCode = $zip            
$user.c = $country            
$user.SetInfo()


To keep everything consistent I’ve set the address information as a set of variables. The street address is a here-string so we can get multiple lines into the address. The country is designated by the 2 letter ISO code – be careful with this. I tend to set in the GUI and then check the property value in ADSIEdit if I have any doubts.



This is a totally made up address



The Microsoft cmdlet has a parameter for each element of the address. The Quest cmdlet is the same except, bizarrely, the country



The provider and script we have to set each property. Notice that some properties are not intuitive:



  • l = city
  • st = state
  • c = country

PowerShell Deep Dive 2012

The 2012 PowerShell Deep Dive has been announced  - April 29 – May 2 in San Diego.

http://blogs.msdn.com/b/powershell/archive/2012/01/27/it-s-time-for-another-powershell-deep-dive.aspx

 

This time PowerShell is a full track so expect more of your favourite stuff.  Hope to see you there.

Restricting a user’s computers

On the Account tab of an AD user’s properties there is a button labelled “Logon to …” that can be used to restrict the computers a user can logon onto. The default is that users can log onto any machine. If you want to script the restriction

if (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}            
            
$ou = "OU=England,DC=Manticore,DC=org"            
$ws = "Comp1,Comp2,Comp3,Comp4,Comp5"            
            
"`nMicrosoft"            
$name = "UserA"            
Get-ADUser -Identity $name |            
Set-ADUser -Replace @{userWorkstations = $ws}            
            
"`nAD provider"            
$name = "UserB"            
$dn = "cn=$name,$ou"            
Set-ItemProperty -Path AD:\$dn  -Name userWorkstations -Value $ws -Force            
            
"`nQuest"            
$name = "UserC"            
Get-QADUser -Identity $name |            
Set-QADUser -ObjectAttributes @{userWorkstations = $ws}            
#>            
"`nScript"            
$name = "UserD"            
$dn = "cn=$name,$ou"            
$user = [adsi]"LDAP://$dn"            
$user.userWorkstations = $ws            
$user.SetInfo()


Create a list on computer names – notice that there aren’t any spaces between the computer names – this is required.



Otherwise the scripts work in the same way as setting any other property

Integer sizes

 

If you’ve used PowerShell for any time you will be away of [int] meaning integer. One common use is in functions to define a parameter’s data type

function test1 {
param (
  [int]$a,
  [int]$b
)
$a * $b
}

We can use this function

PS> test1 -a 10 -b 6
60

OK simple stuff but what if we do this

PS> test1 -a 2147483648 -b 17
test1 : Cannot process argument transformation on parameter 'a'. Cannot convert
value "2147483648" to type "System.Int32". Error: "Value was either too large
or too small for an Int32."
At line:1 char:9
+ test1 -a <<<<  2147483648 -b 17
    + CategoryInfo          : InvalidData: (:) [test1], ParameterBindin...mati
   onException
    + FullyQualifiedErrorId : ParameterArgumentTransformationError,test1

Oh

Integers come in a number of different sizes – denoted by the number of bits that are used to store the number – 16, 32 and 64 respectively.  The standard [int] is a 32bit integer (4 bytes)

We can see the maximum and minimum numbers that can be stored in these data types using the MaxValue and MinValue properties

"`n16 bit integer"
"$([int16]::MinValue) to $([int16]::MaxValue)"

"`n32 bit integer"
"$([int32]::MinValue) to $([int32]::MaxValue)"

"`n32 bit integer alternative"
"$([int]::MinValue) to $([int]::MaxValue)"

"`n64 bit integer"
"$([int64]::MinValue) to $([int64]::MaxValue)"

 

which gives these results

16 bit integer
-32768 to 32767

32 bit integer
-2147483648 to 2147483647

32 bit integer alternative
-2147483648 to 2147483647

64 bit integer
-9223372036854775808 to 9223372036854775807

 

So 2147483648 is one bigger than the maximum value storable in 32 bit integer. We could use a 64bit integer or we can use an unsigned integer. This only contains positive values

"`nunsigned 16 bit integer"
"$([uint16]::MinValue) to $([uint16]::MaxValue)"

"`nunsigned 32 bit integer"
"$([uint32]::MinValue) to $([uint32]::MaxValue)"

"`nunsigned 64 bit integer"
"$([uint64]::MinValue) to $([uint64]::MaxValue)"

 

unsigned 16 bit integer
0 to 65535

unsigned 32 bit integer
0 to 4294967295

unsigned 64 bit integer
0 to 18446744073709551615

Which should you use?  Use int64 if likely to have negative values and possibly uint32 if definitely only positive values

Setting a users logon hours

By default a user can logon 24/7.  Is this acceptable – should users be able to logon during the night or weekends. AD Users and Computers has a GUI to set the hours users can logon. But we don’t need a GUI we can do this

if (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}            
            
$ou = "OU=England,DC=Manticore,DC=org"            
            
## allow logon 8am - 6pm Monday to Friday            
[byte[]]$hours = @(0,0,0,0,255,3,0,255,3,0,255,3,0,255,3,0,255,3,0,0,0)            
            
"`nMicrosoft"            
$name = "UserA"            
Get-ADUser -Identity $name |            
Set-ADUser -Replace @{logonhours = $hours}            
            
"`nAD provider"            
$name = "UserB"            
$dn = "cn=$name,$ou"            
Set-ItemProperty -Path AD:\$dn  -Name logonhours -Value $hours -Force            
            
"`nQuest"            
$name = "UserC"            
Get-QADUser -Identity $name |            
Set-QADUser -ObjectAttributes @{logonhours = $hours}            
            
"`nScript"            
$name = "UserD"            
$dn = "cn=$name,$ou"            
$user = [adsi]"LDAP://$dn"            
$user.logonhours[0] = $hours            
$user.SetInfo()


We’ll use the four test users we created earlier in the England OU.



The important point is how we represent the hours users can logon.



The information is stored as a byte array – 3bytes per day with 1 bit per hour



We want to restrict the users to 8am-6pm Monday to Friday so we use



[byte[]]$hours = @(0,0,0,0,255,3,0,255,3,0,255,3,0,255,3,0,255,3,0,0,0)



Sunday is the start of the week – no logons allowed so first three bytes are 0. Monday’s three bytes starts with a 0 as don’t want logons between midnight and 8am. The 8 hours of logons followed by two hours. etc



A few other examples might help



Deny all logon



[byte[]]$hours = @(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)



 



Allow logon at all hours



[byte[]]$hours = @(255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255)



 



Allow 8am-6pm – 7 days a week



[byte[]]$hours = @(0,255,3,0,255,3,0,255,3,0,255,3,0,255,3,0,255,3,0,255,3)



 



If you are in doubt about generating the array – set in the GUI then copy the values using ADSIEdit



The scripts are straightforward – the cmdlets get the user and pipe to set. The difference is the parameter we use



The provider uses Set-Itemproperty and the only oddity in the script is we use logonhours[0] as the property. This forces acceptance of the byte array

Creating an OU

Creating an OU is a fairly common activity. The GUI is quick enough for a single OU but we need PowerShell for bulk creation.

Its a straight forward activity

if (-not (Get-Module ActiveDirectory)){            
  Import-Module ActiveDirectory            
}            
            
$ou = "OU=BlogTests,DC=Manticore,DC=org"            
            
"`nMicrosoft"            
$name = "OUM"            
New-ADOrganizationalUnit -Path $ou -Name $name            
            
"`nAD provider"            
$name = "OUP"            
New-Item -Path AD:\$ou -Name "OU=$name" -ItemType Directory            
            
"`nQuest"            
$name = "OUQ"            
New-QADObject -ParentContainer $ou -Type 'OrganizationalUnit' -NamingProperty 'ou' -Name $name            
            
"`nScript"            
$name = "OUS"            
$target = [adsi]"LDAP://$ou"            
$user = $target.Create("organizationalunit", "ou=$name")            
$user.SetInfo()


There is a Microsoft cmdlet specifically for this activity. Just give it a path and a name.  The Quest cmdlet is marginally more complicated but still easy.



The script you will recognise as a variant of creating a user. This pattern works for most things in ADSI – get the parent container and use the Create() method.



The provide uses New-Item as you would expect – notice the ItemType is Directory!



Alternatively the provider supplies the ultimate shortcut code for creating an OU.  Yes md (or mkdir) work



cd ad:\$ou
md "ou=testing"