Preventing non admin accounts being added to a group using Active Roles

Here is a function I use to prevent non admin users being added to a group

You can modify this to get the user object and then take appropriate action –

In my case the DN was CN=admin- so I realised I didn;t need to actuially bind to the user object but I left in my original code should I need it later – you can see my original get-qaduser call is commented out

# $member will be the DN
# $user = Get-QADUser $member

NOTE: if you are allowing changes externally to the group as is the case in my environment you also need an onPostModify ($Request)  function to deal with changes syncing in from other DCs.

function onPreModify($Request)
{
# this function handles updates carried out in ARS – prevents non admin user being added
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> Check if this was a group modification”
if ($Request.class -eq “group”)
{
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> Group object modified”
if ( $(Get-IsAttributeModified -AttributeName ‘member’ -Request $Request) )
{
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> Checking if member attribute was modified”
for ($i = 0; $i -lt $Request.PropertyCount; $i++) {
$item = $Request.Item($i)
$Name = $item.Name
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> item = $($item) name = $($name)”
# enumerate the objects attributes until we locate the “member” attribute
if ($Name -eq “member”)
{
# check that the member attribute was modified
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> Found Member Attribute so processing ”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> item control code = $($item.ControlCode) ”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> Constant APEND =  $($Constants.ADS_PROPERTY_APPEND) ”
if($item.ControlCode -eq $Constants.ADS_PROPERTY_APPEND )
{
# iterate through the group members
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> item control code was APPEND so processing…… ”
foreach ($member in $item.Values) {
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> member = $($member)”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> left(member,9) = $($member.substring(0,9))”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> left(member,10) = $($member.substring(0,10))”
# $member will be the DN
#$user = Get-QADUser $member
#if ( $($user.sAMAccountName).substring(0,6) -ne “admin-” )
if ( ( $member.substring(0,9) -ne “CN=Admin-” ) -and  ( $member.substring(0,10) -ne “CN=Admin -” ) -and ( $member.substring(0,7) -ne “CN=svc-” ) ) {
$groupName = Get-AttributeValue -AttributeName “Name” -ADSIObject $Request
$groupDN = Get-AttributeValue -AttributeName “DN” -ADSIObject $Request
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PreModify >>> >> Grp Name : $($groupname) DN: $($groupDN)”
throw “******************************************************`nYou cannot add non ‘admin-<logon name>’ accounts `nto this group`n******************************************************`nNOTE: Script Policy Checks the DN of the user not the logon name”
}
} # end for each
} # end if property changed
} # end if member found
} # end for iterate group members
} # end Check if group members updated
} # end if modified object was a group
} # end function

 

function onPostModify ($Request)
{
# this fucntion deals with changes made outside of ARS…
if ($Request.class -eq “group”)
{
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> Group object modified”
if ( $(Get-IsAttributeModified -AttributeName ‘member’ -Request $Request) )
{
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> Group membership changed”
for ($i = 0; $i -lt $Request.PropertyCount; $i++) {
$item = $Request.Item($i)
$Name = $item.Name
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> item = $($item) name = $($name)”
# enumerate the objects attributes until we locate the “member” attribute
if ($Name -eq “member”)
{
# check that the member attribute was modified
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> Found Member Attribute so processing ”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> item control code = $($item.ControlCode) ”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> Constant ADS_PROPERTY_APPEND =  $($Constants.ADS_PROPERTY_APPEND) ”
if( $item.ControlCode -eq $Constants.ADS_PROPERTY_APPEND )
{
# iterate through the group members
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> item control code was APPEND so processing…… ”
foreach ($member in $item.Values) {
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> member = $($member)”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> left(member,9) = $($member.substring(0,9))”
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only PostModify >>> left(member,10) = $($member.substring(0,10))”
# $member will be the DN
if ( ( $member.substring(0,9) -ne “CN=Admin-” ) -and  ( $member.substring(0,10) -ne “CN=Admin -” ) -and ( $member.substring(0,7) -ne “CN=svc-” ) ) {
$groupName = Get-AttributeValue -AttributeName “Name” -ADSIObject $Request
Out-DebugString -verbosity 9 -str “group-Enforce-Admin-Only postModify >>> GroupName = $($groupname)”
Remove-QADGroupMember -Member $member -Proxy -Identity $groupName -Control @{OperationReason=”Group – Enforce Admin Users Only_v1.1″}
} # end check if the user is not an admin- user
} # end processing each member added
} # end if members were added – ADS_PROPERTY_APPEND = 3
} # end if member found
} # end for iterate group members updated
} # end check that the group membership was updated
} # end check that request object was a group object
}

I’ve included my debug function for you here too – this writes to the event log – these go at the top of the script file

function onInit($Context)
{
$par01 = $context.AddParameter(“debugging”)
$par01.MultiValued = $false
$par01.PossibleValues = “0”, “1”, “2”, “3”, “4”, “5”, “6”, “7”, “8”, “9”
$par01.DefaultValue = “1”
$par01.Description = “Debugging EventLog Level where: 0 is no debugging; 9 is the most verbose; 1 is the least verbose”
$par01.Required = $false
#
#
}

function Out-DebugString([int]$verbosity, [string]$str )
{
# outputs debug info to the EDM event log
if ( [string]$PolicyEntry.Parameter(“debugging”) -ne ‘0’ )
{
$strDebuggingSwitch = [string]$PolicyEntry.Parameter(“debugging”)
if ( $verbosity -le [int]$strDebuggingSwitch )
{
$EventLog.ReportEvent(2,$str)
}
}

function Get-IsAttributeModified ([string]$AttributeName, $Request)
{
$objEntry = $Request.GetPropertyItem($AttributeName, $Constants.ADSTYPE_CASE_IGNORE_STRING)
if ($objEntry -eq $null) { return $false }
if ($objEntry.ControlCode -eq 0) { return $false }
return $true
} #– Get-IsAttributeModified

Advertisements

How to add a reason to the ActiveRoles Change History

One thing I’d recommend you do is add a “reason” to the ARS change history when ever a policy applies some automated changes.  When you look at the Change History in ARS for an automated update that was applied by a script policy how will you know “why” the attribute was changed unless you do this.

It’s such a simple thing to do too.  Most of the ARS AD cmdlets, like set-qaduser have a control parameter that can be used to add a reason for the change.  To add a reason all you need to is add this command line switch.

-Control @{OperationReason=”SeparationOfDuties_v$Global:scriptVersion”}

Note how I also included a variable in there – this allows me to see not only the script that was run but the version number of the script.  Now the History will show you the reason why an update was made.  Nice don’t you think?

History

 

Using a managed Unit for highlighting incorrectly set up accounts

I’ve been using a script that I got from the Quest ( now Dell ) support site for dynamically building a query for a Managed Unit to show inactive accounts.  The thought occurred to me that I could do this to highlight accounts that have not been set up properly.

Yes I know ARS should do this when the service desk are creating the users – but what if someone manually create an account using ADU&C or a script then the integrity rules enforced by ARS are not always adhered to.

It’s also nice to have a quick way of seeing these anomalies.

Anyway, for what ever reason you use this idea the point is to explain how to update a managed unit dynamically.

A Managed Unit is an Active Roles Dynamic OU an object can only be in one OU in AD but it can be in multiple Managed Units in ARS.  It’s a brilliant Idea!  You can then apply permission templates or ARS policies to control how the objects are managed and setup.

Why would I want to dynamically manage the “filter” used to build the Managed Unit?  Well two examples already alluded to above are where one of the filter variables is based on a date, e.g. accounts not used for 30 days – accounts created in the last 30 days etc.

Here’s how to do it – using powershell 🙂

Create a Managed Unit and set the filter.  e.g.
(&(sAMAccountType=805306368)(!(|(employeeID=*)(employeeNumber=*)(sAMAccountName=svc*)(title=*_*)(secretary=*)(userAccountControl:1.2.840.113556.1.4.803:=2)))(whenCreated>=19990224103016.0Z))

or

(&(employeeID=*)(sAMAccountType=805306368)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(lastLogonTimestamp<=130749192000000000))

The interesting bit in both these examples is the date.  These numbers represent a time.  Lastlogon using a lareg integer – you convert this using this formula:  $objLargeInteger90 = $(Get-Date).Date.AddDays(-90).ToFileTime().

The whenCreated attribute uses a simpler string which is human readable YYYYMMDDHHMMSS.OZ

So what we need is a script to dynamically build these queries once a day and then update the managed unit filter.  Hopefully the below needs little explanation and you can easily modify it for your environment.

$Rule.Base can be used to narrow down the search – I search all of my managed domains in this example.

How did I figure out the rule type?  Well I didn’t really what I did was examine the existing setting and then reused it by examining the value in $RuleCollection when debugging my script.

$ManagedUnitDN = “CN=New Accounts Incorrectly Tagged Created in the Last 30 Days,CN=Admin Action Required,CN=User Management,CN=Managed Units,CN=Configuration”
$ManagedUnit = [ADSI]”EDMS://$ManagedUnitDN”
$RuleCollection = $ManagedUnit.MembershipRuleCollection
$daysAgo = 30
$dateThreshold = Get-Date( $(Get-Date).adddays(-$daysAgo) ) -uformat %Y%m%d%H%M%S.0Z
do {
$RuleCollection.RemoveAt(0)
} while ($RuleCollection.Count -gt 0)
$Rule = New-Object -ComObject “EDSIManagedUnitCondition”
$Rule.Base = “EDMS://CN=Active Directory”
$Rule.Filter = “(&(sAMAccountType=805306368)(!(|(employeeID=*)(employeeNumber=*)(sAMAccountName=svc*)(sAMAccountName=saPLON*)(sAMAccountName=saBLON*)(title=*_*)(secretary=*)(userAccountControl:1.2.840.113556.1.4.803:=2)))(whenCreated>=$dateThreshold))”
$Rule.Type = 1
$RuleCollection.Add($Rule)
$ManagedUnit.SetInfo()

You can add multiple rules by repeat the lines of code that set the $rule attribute and then add the rule to the collection.
I did a little research to discover the possible rulecollection types and came up with this:

The BASE defines the scope of the search in all of the rulecollection types where one is needed.  The path uses EDMS://

Type 1 is include by query and requires an LDAP query to define the objects to include
Type 2 is exclude by query and requires an LDAP query to define the objects to exclude
Type 3 is include explicity and does not require a filter.  The Base is the DN of the object to include
Type 4 is exclude explicity and does not require a filter.  The Base is the DN of the object to include
Type 5 is include group members and does not require a filter.  The Base is the DN of the group to include
Type 6 is exclude group members and does not require a filter.  The Base is the DN of the group to exclude
Type 7 is user to keep Deprtovisioned users in the group.  The filter is set to (edsvaDeprovisionStatus=*)

Cannot Connect to ARS server using the -proxy switch

ARS server does not exist or could not be contacted

Yeah sure! This is the type of error that on the wrong day will bring out the Basil Fawlty in all of us.

If you are running Active Roles make sure you get the correct version of the cmdlets to match – I’ve listed the versions in a table below to save you searching for it – I can google that for you 🙂 .

If you load the latest cmdlets I’m sure you will be bristling with pride with all the clever stuff you can do with them….. but you won’t be communicating with your ARS service because it will ignore you completely. Also annoyingly the error message will be next to useless, see below two possible errors you will see. Worse the reason for the connection failure may not even be the version of the cmdlets you are running. I’m sure there is a tech note somewhere explaining that the -proxy switch only works when the MMC is installed – perhaps someone can google that for me, as I was unable to find the link to post here.

So you have three things to check. The ARS server version you are running, the cmdlet version and is the MMC installed.

unabletoconnect14

unabletoconnect

How then do I check these things?

Manually it’s easy – open the ARS MMC ( check one item off the list 🙂 Assuming the MMC can connect, help about will confirm the version No. for you. Now open the ActiveRoles Management Shell for Active Directory and the version number will be displayed at the top of the window. Now you can check the table below to ensure the versions all match up and they can start their play date together.

You wont get in this mess if you install the MMC first as it won’t let you install an incompatible version of the cmdlets but the reverse doesn’t happen. The MMC will happily install on top of any version of the cmdlets.

What if I wanted to check in a script which version of the cmdlets were installed as this script might be run on multiple PCs or Servers where the cmdlets might not even be installed. That’s relatively simple too and I’ll bundle that info into a combined post explaining how to find out the PowerShell version you are running as well.

Now where did I leave the keys to the Austin 1100 Sybil must be wondering where I am…

Date Posted Name Version File Type Size
May 16 2011 ActiveRoles Management Shell for Active Directory 32-bitAD Management Shell 1.5.1 is compatible with only ARS 6.7.0 1.5.1 20.08 MB
Oct 29 2012 Quest One ActiveRoles Management Shell for Active Directory 32-bit – ZipAD Management Shell 1.6.0 is compatible with only ARS 6.8.0 1.6.0 21.95 MB
May 16 2011 ActiveRoles Management Shell for Active Directory 64-bitAD Management Shell 1.5.1 is compatible with only ARS 6.7.0 1.5.1 32.73 MB
Oct 29 2012 Quest One ActiveRoles Management Shell for Active Directory 64-bit – ZipAD Management Shell 1.6.0 is compatible with only ARS 6.8.0 1.6.0 36.79 MB