AB: Address List is not updating

Every time working with Exchange issues is completely new adventure. So it was with this one as well.

Custom address book wasn't updating members. Address book was configured to update member based on specific string in CustomAttribute5.

First thing to check was actual address list and filter that list is using:

 Get-AddressList "Test Address List" | fl *

Container                    : \
Path                         : \Test Address List
DisplayName                  : Test Address List
Name                         : Test Address List
RecipientFilter              : ((CustomAttribute5 -eq 'Test String') -and (Alias -ne $null))  
LdapRecipientFilter          : (&(extensionAttribute5=Test String)(mailNickname=*))
LastUpdatedRecipientFilter   : ((CustomAttribute5 -eq 'Test String') -and (Alias -ne $null))
RecipientFilterApplied       : True
IncludedRecipients           :
ConditionalDepartment        : {}
ConditionalCompany           : {}
ConditionalStateOrProvince   : {}
ConditionalCustomAttribute1  : {}
ConditionalCustomAttribute2  : {}
ConditionalCustomAttribute3  : {}
ConditionalCustomAttribute4  : {}
ConditionalCustomAttribute5  : {}
ConditionalCustomAttribute6  : {}
ConditionalCustomAttribute7  : {}
ConditionalCustomAttribute8  : {}
ConditionalCustomAttribute9  : {}
ConditionalCustomAttribute10 : {}
ConditionalCustomAttribute11 : {}
ConditionalCustomAttribute12 : {}
ConditionalCustomAttribute13 : {}
ConditionalCustomAttribute14 : {}
ConditionalCustomAttribute15 : {}
RecipientContainer           :
RecipientFilterType          : Custom
Identity                     : \Test Address List
IsValid                      : True
ExchangeVersion              : 0.1 (8.0.535.0)
Id                           : \Test Address List

After that we wanted to make sure that we are getting recipients while querying that RecipientFilter:

 Get-Recipient -RecipientPreviewFilter { ((CustomAttribute5 -eq 'Test String') -and (Alias -ne $null)) }

Name     RecipientType
----     -------------
John Doe MailContact

Filter is returning object. So far so good. Checking recipient address list membership showed only membership to defaults, without custom address book:

 $mailContact = Get-MailContact "John Doe"
$mailContact.AddressListMembership
\All Contacts(VLV)
\All Recipients(VLV)
\Default Global Address List
\All Contacts

Running Update-AddressList manually was updated address list membership:

 Get-AddressList "Test Address List"  | Update-AddressList
$mailContact = Get-MailContact "John Doe"
$mailContact.AddressListMembership
\Test Address List
\All Contacts(VLV)
\All Recipients(VLV)
\Default Global Address List
\All Contacts

Searched eventlogs but nothing strange was reported for address list activities. Looking more deeply what Update-AddressList is doing you can notice calls to "Rus Agent" (Recipient Update Service Agent):

 Get-AddressList "Test Address List"  | Update-AddressList -Verbose
VERBOSE: [11:03:51.097 GMT] Update-AddressList : Runspace context: Executing user: domain.com/Users/Administrator, Executing user organization: , Current organization: , RBAC-enabled: Enabled.
VERBOSE: [11:03:51.425 GMT] Update-AddressList : Active Directory session settings for 'Update-AddressList' are: View Entire Forest: 'False', Default Scope: 'domain.com', Configuration Domain Controller:
'DC.domain.com', Preferred Global Catalog: 'DC.domain.com', Preferred Domain Controllers: '{ DC.domain.com }'
VERBOSE: [11:03:51.441 GMT] Update-AddressList : Beginning processing Update-AddressList
VERBOSE: [11:03:51.456 GMT] Update-AddressList : Instantiating handler with index 0 for cmdlet extension agent "Rus Agent".  
VERBOSE: [11:03:51.456 GMT] Update-AddressList : Instantiating handler with index 1 for cmdlet extension agent "Admin Audit Log Agent".
VERBOSE: [11:03:51.472 GMT] Update-AddressList : Current ScopeSet is: { Recipient Read Scope: {{, }}, Recipient Write Scopes: {{, }}, Configuration Read Scope: {{, }}, Configuration Write Scope(s): {{, }, }, Exclusive Recipient
Scope(s): {}, Exclusive Configuration Scope(s): {} }
VERBOSE: [11:03:51.487 GMT] Update-AddressList : Searching objects "\Test Address List" of type "AddressBookBase" under the root "$null".
VERBOSE: [11:03:51.487 GMT] Update-AddressList : Previous operation run on domain controller 'DC.domain.com'.
VERBOSE: [11:03:51.487 GMT] Update-AddressList : The current object has been processed by the cmdlet extension agent with index 0.
VERBOSE: [11:03:51.487 GMT] Update-AddressList : The current object has been processed by the cmdlet extension agent with index 1.
VERBOSE: [11:03:51.487 GMT] Update-AddressList : Processing object "\Test Address List".
VERBOSE: [11:03:51.487 GMT] Update-AddressList : [Microsoft Cmdlet Extension Agent] Validating object "\Test Address List" of type "AddressBookBase".
VERBOSE: [11:03:51.487 GMT] Update-AddressList : Admin Audit Log: Entered Handler:Validate.
VERBOSE: [11:03:51.487 GMT] Update-AddressList : Admin Audit Log: Exited Handler:Validate.
VERBOSE: Updating all recipients matching the filter of address list "\Test Address List".
VERBOSE: [11:03:51.487 GMT] Update-AddressList : Resolved current organization: .
VERBOSE: [11:03:51.878 GMT] Update-AddressList : Saving object "\Test Address List" of type "AddressBookBase" and state "Unchanged".
VERBOSE: [11:03:51.878 GMT] Update-AddressList : Previous operation run on domain controller 'DC.domain.com'.
VERBOSE: [11:03:51.878 GMT] Update-AddressList : Admin Audit Log: Entered Handler:OnComplete.
VERBOSE: [11:03:52.206 GMT] Update-AddressList : Admin Audit Log: Exited Handler:OnComplete.
VERBOSE: [11:03:52.222 GMT] Update-AddressList : Ending processing Update-AddressList

"Rus Agent" is executed every time recipient related cmdlet is called, which means that every time Set-Recipient (Set-MailContact, Set-Mailbox, etc...) is executed.

Why "Rus Agent" didn't run when Set-MailContact was used (Set-MailContact -CustomAttribute5 'Test String'). Searched admin audit log to see when command was executed to get something to work with:

 Search-AdminAuditLog -Cmdlets Set-MailContact

Returns nothing?

Ran Search-AdminAuditLog without any parameters just to see if anything will be returned:

 Search-AdminAuditLog

RunspaceId         : 7f9ae249-1393-4af6-9705-839faddfd148
ObjectModified     : \Test Address List
CmdletName         : Update-AddressList
CmdletParameters   : {Verbose, Identity}
ModifiedProperties : {}
Caller             : domain.com/Users/User
ExternalAccess     : False
Succeeded          : True
Error              :
RunDate            : 
OriginatingServer  : EXCHANGE (15.00.1130.005)
Identity           : AAMkADNkOWZkMDFkLThiOTQtNDc0My05ZmVlLWViOTdmODkzYWYzOABGAAAAAAAsKgfCgkDVTbCL/MvE2gSABwCf9J8M2CH1SoFNuEBYOhBbAAAAAAEYAACf9J8M2CH1SoFNuEB
                     YOhBbAAAi9M0qAAA=
IsValid            : True
ObjectState        : New

How command Set-MailContact was executed if it's not in audit logs?

Explanation is actually very simple - Set-ADObject. Recipient CustomAttribute5 was updated through Active Directory PowerShell module and because update wasn't done through Exchange console, "Rus Agent" is never called, hence showInAddressBook AD attribute was never updated which caused recipient not showing in custom address list.

Every time you want to make custom address list, make sure you either set filtering attribute values through Active Directory (PowerShell or ADUC) prior creation address list, or update those attributes through Exchange management shell afterwards.