Lync Server 2013 Address Book Deltas Not Being Produced – but a Fix is Coming!

We’re starting to deploy Enterprise Voice (Lync as a telephone replacement), and it’s been going reasonably well, except for one little thing that’s become a huge headache: Lync’s address book was not getting updated.

 

First attempted solution: Change Address Book full download frequency (“KeepDuration”) from 30 days to 1 day in Set-CsAddressBookConfiguration

Result: No change
Why: It would take the clients at least 30 days before they would start downloading full copies. In our case, that was probably a good thing (see “Third attempted solution”). Greig Sheridan gives a more detailed explanation of why this totally won’t work and some other things you shouldn’t bother trying.

 

Second attempted solution: Switch AddressBookAvailability in Client Policy for everyone to “WebSearchOnly”

Result: Worked like a champ for Lync 2010 and Lync 2013 clients, no change for Office Communicator 2007 R2 clients (the vast majority of non-Enterprise Voice users)
Why: Office Communicator 2007 R2 doesn’t have the ability to do Addess Book Web Queries (ABWQ). This is incorrectly stated in the main TechNet article about new features in Lync Server 2010, but correct in a similar table in this TechNet Magazine article. Grrr…

Additionally, Lync Server ClientPolicy settings do not have any effect on Office Communicator 2007 R2 clients. Those changes have to be made in the client’s Registry or via Group Policy. Since Office Communicator 2007 R2 cannot do Address Book Web Queries, there is no such Registry entry present or possible.

 

Third attempted solution (DO NOT DO THIS): Raise the MaxDeltaFileSizePercentage from the default 20 to 100.

Result:This will produce a delta file and keep it available no matter how large it is. Sites around the world that do not have superb MPLS (WAN) connections had to block all traffic from the Lync pool on Port 443 until we removed the deltas.
Why: all the OCS (and Lync) clients started downloading the deltas as soon as the users logged into them.
Why we thought this might be a good idea: Listed as a solution in the TechNet Forums thread LYNC 2013 generating Full Address book files but not the delta ones
Why it might possibly be a good idea for you: None of your sites are bandwidth-constrained, or all of your users are on their own connections
Why it is most likely not a good idea for you: The delta files can be twice the size of the “full” address book. Multiply that by the number of users at each site, and decide if that is a bandwidth hit you can take in the hour after everyone gets to work.

 

Fourth attempted solution: Made use of one of the Software Assurance (SA) support incidents we got as a Volume Licensing customer

Result: It is a known error in the Lync Server software itself, and a fix is expected in the next Cumulative Update, expected later in June or in July 2014. Hooray! We even get the incident refunded, because Microsoft agrees that it was a problem with the product itself. Keep an eye on the Lync Cumulative Updates

After loads of testing on both our side and theirs, we figured out that ABServer always tries to create deltas each day between the newest full address book (F-xxxx.lsabs) and each of the previous 29 full address book files, but, depending on the MaxDeltaFileSizePercentage, stops making the .lsabs.tmp file and deletes it. For us, the magic “keep” threshold appears to be 52; there is no difference in the delta file size, though.

There are various reports around the web of this being a problem in environments where there are more than 50,000 address book entries – environments like ours. Using ABServer -DumpFile to crack open the C-xxxx-xxxx.lsabs files created (and retained) when we had the MaxDeltaFileSizePercentage set above 52, we discovered that the first part of the file was a complete copy of the address book, while the last part was a long list of “deleted” entries. The GUIDS on the “deleted” entries list matched GUIDS in the “new” entries. The Microsoft Support engineer I’ve been working with described this as a problem comparing entries for large address books.

 

Morals of the story

  • The testing environment has to mirror your production one in data size and scope. This is a problem that does not exist in smaller environments, and, according to Microsoft Support, doesn’t affect Lync Server Standard Edition.
  • Whenever you change anything about Lync’s configuration, think about bandwidth.
  • Don’t believe everything you read on the Internet. Even on TechNet’s official product pages…
  • If you are a Volume Licensing customer (which you probably are if you’re running Lync Server on premises), make use of your Software Assurance incidents. If you don’t know whether you have any, get the person who signs your Volume Licensing agreement to sign into the Volume Licensing Center and grant someone in Operations the right to manage contacts for Software Assurance. Don’t let those incidents go to waste – your company has already paid for them!
  • Just because something is a “known defect,” it doesn’t mean that we have to live with it forever or until the next major version. Be persistent; Microsoft listens sometimes :)

Get-QADUser -ProxyAddress needs the address type or a wildcard

The wonderful colleague who has succeeded me as the main ActiveRoles Server admin here at Awesome German Auto Parts Manufacturer (awesome.com) ran into trouble last week while trying to find out which user had a proxy address that she was trying to assign. firstname.lastname@awesome.com is not quite as unique as one might think, even in a country with loads of rather long family names, and when a holder of a firstname.lastname@awesome.com leaves, there’s possibly another who would like to have it, and who outsiders are trying to reach at that address.

So my successor cleverly noticed that Get-QADUser has a -ProxyAddress parameter, so tried this on the stubborn address:

Get-QADUser -ProxyAddress "firstname.lastname@awesome.com"

Nothing.

She tried it on her own address, as she was quite sure that one was in there.

Nothing.

Resourceful lady that she is, she started Googling.

Nothing beyond this old Dmitry Sotnikov post: http://dmitrysotnikov.wordpress.com/2010/08/13/manage-email-addresses-without-exchange-cmdlets/

His example shows the address type (in this case, x400) prefix on the searched-for address, but doesn’t state that this is necessary for this parameter to work properly.

Because I’d hit my own wall on Lync emergency calling configuration, I experimentally tried:

Get-QADUser -ProxAddress "smtp:myfirst.mylast@awesome.com"

Result!

Just to check, I tried it without whispering “smtp:“, and…

Nothing.

My colleague verified this, and then reminded me that there was this Exchange quota data-gathering script I’d promised to help with a few weeks ago… but that’s another post.

Fun facts about the ProxyAddresses Active Directory attribute and Get-QADUser -ProxyAddress:

- ProxyAddresses is a multi-valued attribute, and -ProxyAddress will search each value in all the ProxyAddresses attributes of ActiveRoles-managed AD objects until it finds all matches.

- Wildcards (“*logonname@awesome.com“) take longer (20 seconds here) than exact matches like “smtp:logonname@awesome.com” (less than a second), but do work. Get-QADUser always returns user objects, and it will return the object only once per query, even if there are several matches within a user’s ProxyAddresses attribute. A wildcard in the middle of the search (“sip:*logonname@awesome.com“) will also work, but takes AGES (2.5 minutes in my environment).

- Since the ProxyAddresses attribute also contain EUM (Exchange UM) and SIP addresses (Lync or other Unified Communications platform), Get-QADUser -ProxyAddress is also useful for finding the source of a collision there. The wildcard is particularly useful here: “*:firstname.lastname@awesome.com” will find all the objects that contain that address, regardless of type.

- It is case-insensitive. That means that it will match both “smtp:firstname.lastname@awesome.com” (a secondary proxy address) and “SMTP:firstname.lastname@awesome.com” (the user’s current primary email address).

- Get-QADUser -PrimaryProxyAddress is picky in exactly the same way: Get-QADUser -PrimaryProxyAddress "firstname.lastname@awesome.com" didn’t work; Get-QADUser -PrimaryProxyAddress "smtp:firstname.lastname@awesome.com" did. The performance differences between wildcard and exact match searches were in line with -ProxyAddress as well.

The following one-liner finds all the users with *.mueller@awesome.com proxy addresses, no matter what type, and makes a flat (String) MatchingAddresses attribute for the ones with more than one match (for example, sip:max.mueller@awesome.com and smtp:max.mueller@awesome.com).

Get-QADUser -ProxyAddress "*.mueller@awesome.com" | select name, @{n="MatchingAddresses";e={($_.proxyaddresses | where { $_ -like "*.mueller@awesome.com"}) -join ';' }}

Manage Lync Users who used to be in Domain Admins

It’s been awhile – I’ve been transitioning into Lync administration these past few months, but ARS is still a part of that…

Today, I tried changing some of my Lync user policies, and got this:

Active Directory operation failed on “DC01234.awesomedomain.com”. You cannot retry this operation: “Insufficient access rights to perform the operation 00002098: SecErr: DSID-03150BB9, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0″.You do not have the appropriate permissions to perform this operation in Active Directory. One possible cause is that the Lync Server Control Panel and Remote Windows PowerShell cannot modify users who belong to protected security groups (for example, the Domain Admins group). To manage users in the Domain Admins group, use the Lync Server Management Shell and log on using a Domain Admins account. There are other possible causes. For details, see Lync Server 2010 Help.

But… my account isn’t in Domain Admins! It was once, for about five minutes while I attempted to prove a point, but that was several months ago.

However, that is enough to mark the account to Active Directory FOREVER AND EVER as being “special”.

Dave Simm has a post that explains what happens and how to fix it: Lync enabling or making Lync changes to a user who is or was a member of the Domain Admins security group

However, one of the commenters, Rikard Strand, points out that the inheritance fix might be automatically reverted due to the fact that adminCount is still 1.

So, here’s how you fix this with ARS:

1) Find the Lync-enabled users who also have adminCount=1 – this doesn’t mean that they definitely have the inheritance issue, but that they might:

Get-QADUser -SearchAttributes @{adminCount=1;'msrtcsip-PrimaryUserAddress'="*"}

2) Go remove them from Domain Admins or disable them for Lync if they should stay in Domain Admins - you shouldn’t be using a Domain Admin account to run your desktop applications!

3) If they’re going to remain Lync users, fix the AD object security permissions inheritance as described in Dave’s post (dsa.msc – advanced view – Security – Advanced - check the “inherit” box)

4) Finally, set the adminCount for the users you just fixed inheritance for:

Set-QADUser username@domain.com -ObjectAttributes @{adminCount=0}

IT News for Your Ears – Tech Podcasts You Should Be Listening To

A colleague on the management team in my department insists on taking a few hours as soon as the biweekly issue of c’t arrives. If you’re reading this blog, you’re probably a time-pressed administrator who, like me, doesn’t have that luxury. However, it is part of our jobs to keep up on the wider IT world.

I listen to several podcasts each week during the drive to work – much safer than reading an IT magazine while on the road. These shows also help me put some much-needed miles on my bike :)

I’ve included links to the show pages for each of my favorites, and for your convenience, an RSS link for copying into your player. If you’re looking for such an app, Car Cast is a nice, simple Android podcast player that is free, but once you’ve installed the free version, top it up with the paid version to get rid of the little ads and show your appreciation for this fine app.

Essential listening for Microsoft IT Pros

  • PowerScripting with Jonathan Walz and Hal Rottenberg: Love scripting or hate it, PowerShell has become a core skill for Active Directory, Exchange and SharePoint administrators, SQL Server DBAs and .NET developers. I started listening to PowerScripting shortly after I discovered PowerShell, and it really helped me along. These days, I mostly listen to it to find out about new applications for it, but still pick up tips and tricks from Jon, Hal and their guests. If you’re just getting started with PowerShell, download and listen to the first 30 episodes or so in order, along with the week’s latest one so you keep up with the community’s news. It was obvious those first few episodes that Jon, and later Hal, were new to podcasting, but now, it’s a very polished listening experience, so stick with it through those initial shows. [RSS link]
  • RunAs Radio with Richard Campbell: The main Microsoft IT Pro show, covering the wide range of MS enterprise technologies in an interview format. The older episodes are still good, but cover a lot of old news. I’d recommend downloading the most recent 10 or so for breadth, then searching the site for the specific technologies you deal with. [RSS link]
  • .NET Rocks! with Carl Franklin and Richard Campbell: Developer-centric parent show of Run-As Radio that comes out twice a week like clockwork. Good for getting a broader view of what’s going on in the Microsoft world. They also have occasional “Geek Out” episodes covering non-IT techologies for a technical, but not expert, audience. The back catalog is over 800 shows – they’re bound to have hit upon something you’re interested in. [RSS link]
  • HanselMinutes with Scott Hanselman: Scott works for Microsoft, but he covers a broad array of development-related topics, along with some non-IT ones, on his always thought-provoking show. Any random episodes will be worth listening to. [RSS link]
  • The UC Architects: An ensemble of Lync and Exchange MVPs and other experts from around the world who keep us up to date on what is happening with the two main constituents of Microsoft Unified Communications platform, along with how it ties in to Office 365. Lync is becoming a very important secondary, even primary skill for Exchange admins, so if you are working with one platform, you need to at least be aware of what’s going on with the other. I’ve just started listening and am still catching up with previous episodes. Their website gives a very good synopsis of each episode, including when in the program each segment started so you can skip past the bits you’re not as interested in. [RSS link]

Other tech podcasts I like

  • NPR’s Technology Podcast: Highlights stitched together each week from the various technology segments on NPR, primarily covers general consumer tech and social/legal issues related to IT. [RSS link]
  • Engadget Podcast: mostly consumer tech, along with some enterprise IT. Fun. [RSS link]
  • Your Website Engineer with Dustin Hartzler: good listening for anyone who does stuff with WordPress. [RSS link]
  • SQLDownUnder with Greg Low: episodes are sporadically released, but excellent listening for anyone who even occasionally has to deal with SQL Server, with bonus Aussie accent and detailed show transcripts for the bits .  The one about SQL Server Reporting Services with Jessica Moss helped me out a TON when I was getting started with reports. [RSS link]
  • Quirks and Quarks: the CBC’s (Canadian Broadcasting Corporation) weekly science radio show, focusing on Canadians scientists and institutions, but reaching out for the week’s big stories. Bob McDonald, the longtime host, is possibly the best science interviewer in the business. [RSS link]

Any podcasts I’m missing? Anyone ready to start that ActiveRoles Server podcast?

Why would I want to deprovision an Active Directory Group?

Most of my posts are about issues I’ve run into on the job. However, I occasionally look at the search terms that brought people here, and saw the question, “why would I want to deprovision a group?”

Deprovisioning in ActiveRoles Server is mostly focused around user accounts, because those are what most visibly consume resources: mailboxes (Exchange CAL), space on a file server, an ARS license (ARS licensing is completely based on the number of enabled User objects) and are most subject to abuse if there’s no legitimate need for them anymore. You can copy and then modify the sample “Built-in Policy – Default Deprovisioning” that ships with ARS. I recommend that you copy any Built-In Policy and change your copy.

However, groups can also be deprovisioned. Deprovisioning lets you take a group out of action without deleting it. What “out of action” means depends on exactly what groups do in your environment, and what information you need to retain. Do you need to keep the membership list of the group? Do you want to be able to “undeprovision” it, bringing it quickly back into use? Is there any data in the Description or Info (Notes) attributes that other systems use for accounting? Do you need a record of who the secondary owners were? Should a group that granted read privileges on a departmental folder be deprovisioned differently from one that was used to give administrator access to the file server that was at an office that has just been closed?

Deprovisioning policy for groups, just like deprovisioning policy for users, can be configured in both the Policy Object wizard, which is a good idea if it’s a simple attribute change based on a pattern or another attribute in the same object, or a script that you then attach to the Policy Object.

You can configure different deprovisioning policies for different OUs, which you then attach to the container you want to apply them to with Policy Object Links, similar to Access Template Links, except for the lack of a snazzy AD Management Shell cmdlet that will let you do them in bulk, like you can with New-QARSAccessTemplateLink.

If you create your Group Deprovisioning policy actions in a script, you put them in the onPreDeprovision(), onDeprovision() and onPostDeprovision() event handlers. Make sure you put your group-related code inside an if ($Request.class -eq “Group”) {  } black to distinguish it from what you want to do when $Request.class -eq “User”. You should do that object class check even if you have separate Policy Objects for deprovisioning user and deprovisioning groups.

As far as whether something should be in onPreDeprovision(), onDeprovision() or onPostDeprovision(), think about what needs to happen before the group goes offline, what attributes you want to be able to restore, and what needs to happen after everything else is finished.

onPreDeprovision() might do checks to see if it is in the local Administrators group for a specific sensitive server, and this has to happen in onPreDeprovision() if converting the group from a Security Group to a Distribution List is part of your deprovisioning process (Distribution Lists do not have SIDs.)

onPostDeprovision() is for clean-up activity: perhaps dynamically determining who the recipients for the notification email should be.

If it is a property change you wish to revert, however, it should be in the onDeprovision() event handler, because that is what is recorded in the data retrieved by that group’s edsvaDeprovisionRecordXML, which is used if you ever tell ARS to undo the deprovisioning.

Once you’ve got your group deprovisioning the way you want it, you might want to use PowerShell to deprovision them the same way you can deprovision users with Deprovision-QADUser. There’s no native Deprovision-QADGroup cmdlet, but I’ve written one, along with UnDeprovision-QADUser and UnDeprovision-QADGroup: Deprovision and UnDeprovision Users and Groups with PowerShell

NetLogon.log: Control what goes in, and get what you want out

Or, when you want some, but not all, of what Netlogon.log has to offer – especially when trying to track down the source of a user account’s lockouts or find subnets that haven’t been put into an Active Directory site yet.

Due to what I no-so-fondly remember as the “NTLM Crisis of ’12″, we have Netlogon turned up to 11 on all our domain controllers – that is, DBFlag is set to 0x2080ffff, just like this TechNet article and everything else you see at first glance on the Internet has it. That quick looks makes it look like your options are EVERYTHING!!1!1 or nothing. Since that one Knowledge Base article literally IS the only place on the Internet where these flags are listed in detail, and Microsoft has recently started featuring the automated “Fix It” solution, here is the info for posterity (and convenience),
nicely formatted into tables.

Data Source: http://support.microsoft.com/kb/109626
Formatting into tables: Amanda Debler (me)
All descriptions as-is from Microsoft.

Basic Netlogon Flags

Flag Name Value Description
NL_INIT 0×00000001 Initialization
NL_MISC 0×00000002 Misc debug
NL_LOGON 0×00000004 Logon processing
NL_SYNC 0×00000008 Synchronization and replication
NL_MAILSLOT 0×00000010 Mailslot messages
NL_SITE 0×00000020 Sites
NL_CRITICAL 0×00000100 Only real important errors
NL_SESSION_SETUP 0×00000200 Trusted Domain maintenance
NL_DOMAIN 0×00000400 Hosted Domain maintenance
NL_2 0×00000800  
NL_SERVER_SESS 0×00001000 Server session maintenance
NL_CHANGELOG 0×00002000 Change Log references
NL_DNS 0×00004000 DNS name registration

Verbose Netlogon Flags

Flag Name Value Description
NL_WORKER 0×00010000 Debug worker thread
NL_DNS_MORE 0×00020000 Verbose DNS name registration
NL_PULSE_MORE 0×00040000 Verbose pulse processing
NL_SESSION_MORE 0×00080000 Verbose session management
NL_REPL_TIME 0×00100000 replication timing output
NL_REPL_OBJ_TIME 0×00200000 replication objects get/set timing output
NL_ENCRYPT 0×00400000 debug encrypt and decrypt across net
NL_SYNC_MORE 0×00800000 additional replication dbgprint
NL_PACK_VERBOSE 0×01000000 Verbose Pack/Unpack
NL_MAILSLOT_TEXT 0×02000000 Verbose Mailslot messages
NL_CHALLENGE_RES 0×04000000 challenge response debug
NL_SITE_MORE 0×08000000 Verbose sites

Netlogon Control Flags

Flag Name Value Description
NL_INHIBIT_CANCEL 0×10000000 Don’t cancel API calls
NL_TIMESTAMP 0×20000000 TimeStamp each output line
NL_ONECHANGE_REPL 0×40000000 Only replicate one change per call
NL_BREAKPOINT 0×80000000 Enter debugger on startup

Right now, I just want a clear look at account lockouts and subnetless IPs, even on our busiest DCs. A very busy DC can blow through a 100MB log file allowance in a few hours, and even with Netlogon.bak, collection and filtering would have to happen several times a day to make sure we see all the bad logons.

“But why would you want to capture LESS diagnostic information?!?” Because heavy logging can cause its own problems – read and think about this article from the Directory Services team before you implement “All Logging, All The Time”. It explains how to enable (and disable) logging for all facets of Directory Services.

As a compromise, I’m just going to turn off all those ‘[SITE]‘ messages, since they are most of the entries in netlogon.log, and don’t provide any information I need right now. So, 0x2080ffff - 0x00000020 = 0x2080ffdf. I tried the nice .NET/PowerShell way, but it failed against a 2003 server. Back to the old-fashioned way – still possible from within a PowerShell script:

(all one line) reg add "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v DBFlag /t REG_DWORD /d 0x2080ffdf /f

Make sure that your log file is the size you want it to be (in this case, 100 megabytes):

(all one line)reg add "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v MaximumLogFileSize /t REG_DWORD /d 100000000 /f

And to check our work…

(all one line) reg query "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v DBFlag

(all one line) reg query "\\$computerName\HKLM\SYSTEM\CurrentControlSet\Services\NetLogon\Parameters" /v MaximumLogFileSize

Finish up by stopping and starting the Netlogon service, again, the old-fashioned way with NT Service Controller. You need to use sc.exe in PowerShell, because sc is an alias for Set-Content:

sc.exe \\$computerName stop netlogon

sc.exe \\$computerName start netlogon

Here’s a table of the result codes to interpret the [LOGON] entries (from Section “Netlogon Log File Error Codes” http://technet.microsoft.com/en-us/library/cc776964.aspx, copied in case that particular link goes away):

Log Code Description
0×0 Successful login
0xC0000064 The specified user does not exist
0xC000006A The value provided as the current password is not correct
0xC000006C Password policy not met
0xC000006D The attempted logon is invalid due to a bad user name
0xC000006E User account restriction has prevented successful login
0xC000006F The user account has time restrictions and may not be logged onto at this time
0xC0000070 The user is restricted and may not log on from the source workstation
0xC0000071 The user account’s password has expired
0xC0000072 The user account is currently disabled
0xC000009A Insufficient system resources
0xC0000193 The user’s account has expired
0xC0000224 User must change his password before he logs on the first time
0xC0000234 The user account has been automatically locked

The most important ones to distinguish between are 0xC000006A (bad password was entered this time – these ARE the droids you’re looking for) and 0xC0000234 (a logon attempt has been made with a user account that has been locked out, but this says nothing about whether this current attempt used a good or bad password)

The cmdlet I use to collect NetLogon logs for analysis on my local machine. It must be used in an Powershell instance that was started as a Domain Admin or other account with access to the DC’s %Windows%\Debug directory. Combine in a pipeline with a list of your DCs to speed it up.


function Get-NetLogonLog {
Param(
$computerName
,$destinationPath="C:\temp\netlogon"
)
  if (-not (Test-Path $destinationPath) ) {
   New-Item -Path $destinationPath -ItemType Directory
  }
  if (Test-Path "\\$computerName\c$\WINDOWS\Debug\Netlogon.log") {
      Copy-Item -Path "\\$computerName\c$\WINDOWS\Debug\Netlogon.log" -Destination "$destinationPath\$computerName-Netlogon.log"
  }
  elseif (Test-Path "\\$computerName\c$\WINNT\Debug\Netlogon.log") {
    Copy-Item -Path "\\$computerName\c$\WINNT\Debug\Netlogon.log" -Destination "$destinationPath\$computerName-Netlogon.log"
  }
  else {
    "Could not find Netlogon.log for $computerName" | Out-File -FilePath "$destinationPath\errors.log" -Append
  }
}

Finally, here are functions to separate the entries I want into different files – separate from the function to gather the NetLogon.log files because it does NOT need to be run as someone with access to your DCs’ c:\Windows\Debug directories…


function Test-NetLogonEntryBadSubnet {
PROCESS {
  if ($_ -like "*NO_CLIENT_SITE*") {
    $_
    }
  }
}

function Test-NetLogonEntryBadLogon {
PROCESS {
  if ($_ -like "*\[LOGON\]*0xC000006A*" -or $_ -like "*\[LOGON\]*0xC0000234*") {
    $_
    }
  }
}

Function Test-NonTransitiveLogon {
Param($PcNamePrefix = "P0")
PROCESS {
  if (($_ -like "*\[LOGON\]*$PcNamePrefix*0x0*") -and ($_ -notlike "*transit*")") {
    $_
    }
  }
}

function Get-NetLogonBadLogonOrSubnet {
PROCESS {
  if ($_ -like "*NO_CLIENT_SITE*" -or $_ -like "*\[LOGON\]*0xC000006A*" -or $_ -like "*\[LOGON\]*0xC0000234*") {
    $_
    }
  }
}

function Write-NetlogonExtracts {
Param(
$directory="c:\temp\netlogon"
, $computerName
, $sourceFile="$directory\$computerName-netlogon.log"
, $targetBadSubnetFile="$directory\$computerName-BadSubnets.txt"
, $targetBadLogonFile="$directory\$computerName-BadLogons.txt"
, $targetDirectLogonFile="$directory\$computerName-DirectLogons.txt"
, $PcNamePrefix = "P0"
)
# Faster to only filter for one match at a time. Not sure why.
  Get-Content $sourceFile | Test-NetLogonEntryBadSubnet | Out-File -FilePath $targetBadSubnetFile
  Get-Content $sourceFile | Test-NetLogonEntryBadLogon | Out-File -FilePath $targetBadLogonFile
  Get-Content $sourceFile | Test-NonTransitiveLogon -PcNamePrefix $PcNamePrefix | Out-File -FilePath $targetDirectLogonFile
}

More on parsing the resulting BadSubnets and BadLogons files later!

ARS Spring Cleaning: Converting Query-Based Managed Unit Memberships to Explicitly-Defined Ones

In ActiveRoles Server, Managed Units (class edsManagedUnit) are virtual Organizational Units, the members of which can be defined explicitly, by membership in some other group, or by LDAP query. You can then assign Access Template Links to give users and/or groups various privileges, either directly on a Managed Unit, or at the Managed Units Container (edsManagedUnitsContainer) level.

Populating a Managed Unit by query is a nice, quick way to get something useful. However, it’s not always the best option, especially if you know that the membership will not change very much. Read Greg Montoya’s article on how this affects your ActiveRoles Server performance, along with several other ARS performance tips.

If the members of a Managed Unit are very static, it’s a good idea to include them explicitly.

“But I don’t WANT to put each of those 300 organizational units in by hand!”

Unfortunately, there isn’t a nice Get-QARSManagedUnit/Set-QARSManagedUnit cmdlet family… yet :)

First, the a few words about anatomy of an edsManagedUnit object.

edsaMember looks like a nice place to add and remove members – the MMC even appears to let you. However, this is an illusion. I added an OU (distinguishedName format) to the edsaMember attribute, but it did not stay in there once I pulled the object again.

edsaMUConditionsList is what REALLY controls this, which is unfortunate, because it is ugly. GUIDs and LDAP queries all around.

Here’s how to decode it:

[] – contains a rule: code for which type of rule, followed by a semicolon (;) and a GUID specifying an object, followed by a second semicolon – if it’s a query rule, then there is an LDAP query on the previously-specified object

Rule codes:

0×1 – query for which objects to include
0×2 – query for which objects to exclude
0×3 – an explicitly included object
0×4 – an explicitly excluded object
0×5 – include the members of this group
0×6 – exclude the members of this group

IDU – include deprovisioned objects

What we want to do is to parse the contents of edsaMUConditionsList, turn all those 0×1 and 0×2 query rules into (possibly) a whole bunch of 0×3 and 0×4 entries, rewrite it all into the expected format, and put that new string into the edsaMUConditionsList value for the Managed Unit. I did not make any re-write rules for group-based Managed Unit membership, simply because I haven’t needed them. Feel free to suggest them!

So, here are my ManagedUnit-related functions so far… if there’s enough interest, I’ll organize them into a little module, including Set-QARSManagedUnit and New-QARSManagedUnit, as well as Backup-QARSManagedUnit, Remove-QARSManagedUnit and Restore-QARSManagedUnit, for MUs you don’t need to have around all the time, but are occasionally very handy.

Please note that you must be connected to ARS as one of the ARS Admins: Use Connect-QARService -Proxy -Credential (Get-Credential) and enter your ARS Admin username/password when prompted. Do not use your regular account as your ARS Admin account – treat your ARS Admin account just like you would a Domain Admin (or Enterprise Admin!) account in AD.


function SplitMUConditionsList {
param($MUConditionsList)
$MUConditionsList.Split(']') | foreach { $_.replace('[','')} | where { $_ -ne '' }
}

function GetQueryResults {
param($searchRoot
, $ldapFilter
)
 Get-QADObject -SearchRoot $searchRoot -LdapFilter $ldapFilter | foreach { $_.GUID }
}

function ParseMUCondition {
param($MUCondition)
 $MUConditionArray = $MUCondition.Split(';')
 switch($MUConditionArray[0]) {
  "0x1" { GetQueryResults -searchroot $MUConditionArray[1] -ldapFilter $MUConditionArray[2] | foreach {"0x3;$_;"} }
  "0x2" { GetQueryResults -searchroot $MUConditionArray[1] -ldapFilter $MUConditionArray[2] | foreach {"0x4;$_;"} }
  Default {$MUCondition}
 }
}

function ReassembleMUConditionsList {
param($rawMUConditions)
 $MUConditionsList = ""
 foreach ($condition in $rawMUConditions) {
  $MUConditionsList += "[$condition]"
 }
 $MUConditionsList = $MUConditionsList | Sort-Object
 $MUConditionsList = $MUConditionsList | Get-Unique
 $MUConditionsList
}

function ConvertTo-ExplicitMUMembership {
param(
$ManagedUnitDN
)
 $ManagedUnit = Get-QARSManagedUnit -distinguishedName $ManagedUnitDN
 if ($ManagedUnit.Type -ne "edsManagedUnit") {
  Write-Error "Not a Managed Unit"
  return
 }
 if (($ManagedUnit.edsaMUConditionsList -eq $null) -or ($ManagedUnit.edsaMUConditionsList -eq '')) {
  $ManagedUnit = Get-QARSManagedUnit -distinguishedName $ManagedUnit.DN
  Write-Host "Had to get it"
 }
 $OldMUConditionsList = $ManagedUnit.edsaMUConditionsList
 $conditions = SplitMUConditionsList($OldMUConditionsList) | foreach { ParseMUCondition $_ }
 $NewMUConditionsList = ReassembleMUConditionsList -rawMUConditions $conditions
 Set-QADObject -Identity $ManagedUnitDN -ObjectAttributes @{edsaMUConditionsList=$NewMUConditionsList}
}

function Get-QARSManagedUnit {
Param(
 [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True,ParameterSetName="ByDN")]$distinguishedName
 , [Parameter(Position=0, Mandatory=$True, ValueFromPipeline=$True,ParameterSetName="ByName")]$name
 , [Parameter(Position=1, Mandatory=$False, ValueFromPipeline=$True,ParameterSetName="ByName")]$searchRoot = "CN=Managed Units,CN=Configuration"
 )
 if ($distinguishedName) {
  Get-QADObject -Identity $distinguishedName -Type edsManagedUnit -IncludedProperties edsaMember, edsaMUConditionsList
 }
 if ($name) {
  Get-QADObject -Name $name -SearchRoot $searchRoot -Type edsManagedUnit -IncludedProperties edsaMember, edsaMUConditionsList
 }
}

Want more QARSManagedUnit cmdlets? Post a comment!