[Webinar] Streamline your web hosting managementRegister Today

x
?
Solved

Set Associated external account property through script

Posted on 2008-02-06
34
Medium Priority
?
2,707 Views
Last Modified: 2012-08-13
Hi
Is there a way to set the "associated external account" property and "full mailbox access" property on the Exchange advanced tab of multiple users, to their respective accounts from a different forest(trust enabled) through script/

Regards
Harsha
0
Comment
Question by:ASAdmin
  • 15
  • 9
  • 7
  • +3
34 Comments
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20840050

But of course :)

I did one the other day, let me dig it out again.

Chris
0
 
LVL 71

Accepted Solution

by:
Chris Dent earned 2000 total points
ID: 20840083
Here we go, this is all in VbScript so it'll need saving as .vbs.

It basically takes a CSV File containing the User names you (one in each domain) then reads the first as the account with the mailbox, and the second as the one you want to set as Associated External Account.

You'll need to fix a few of the Constants at the top for it to work (as it says) otherwise I recommend testing it on a small number of users first (one would be good).

Chris
Option Explicit
 
' The script can find everything else, but it must be told which domains it's to work against.
' Fully qualified domain names for both (domain, not server).
 
Const EXCHANGE_DOMAIN = "exchangedomain.local"
Const EXTERNAL_DOMAIN = "externaldomain.local"
 
' Set the Filename to read usernames from. Expects Comma Delimited List <ExchangeUsername>,<ExternalUsername>
 
Const FILE_NAME = "Filename.csv"
 
'
' Subroutines
'
 
Sub ReadFile
	Dim objFileSystem, objFile
	Dim strLine, strExchangeUsername, strExternalUsername
	Dim arrLine
 
	' Connect to the File
 
	Set objFileSystem = CreateObject("Scripting.FileSystemObject")
	Set objFile = objFileSystem.OpenTextFile(FILE_NAME, 1, False, 0)
 
	' Read all lines in the File, adding each unique username to a Dictionary
 
	Do While Not objFile.AtEndOfStream
		strLine = objFile.ReadLine
		arrLine = Split(strLine, ",")
 
		strExchangeUsername = LCase(arrLine(0))
		strExternalUsername = arrLine(1)
 
		If Not objUsers.Exists(strExchangeUserName) Then
			objUsers.Add strExchangeUsername, Array(strExternalUsername, "")
		End If
	Loop
 
	Set objFile = Nothing
	Set objFileSystem = Nothing
End Sub
 
Sub GetADData
	' Update each of the user accounts in objUsers with a DistinguishedName
 
	Const ADS_SCOPE_SUBTREE = 2
 
	Dim objConnection, objCommand, objRecordSet, objRootDSE
	Dim strUserName, strDN
	Dim arrData
 
	Set objConnection = CreateObject("ADODB.Connection")
	objConnection.Provider = "ADsDSOObject"
	objConnection.Open "Active Directory Provider"
 
	Set objCommand = CreateObject("ADODB.Command")
	objCommand.ActiveConnection = objConnection
 
	' This bit might be slow if the DCs are widely spread over slow connections.
	' May need revision to cope with that.
 
	Set objRootDSE = GetObject("LDAP://" & EXCHANGE_DOMAIN & "/RootDSE")
	objCommand.CommandText = "SELECT distinguishedName, sAMAccountName " &_
		"FROM 'LDAP://" & EXCHANGE_DOMAIN & "/" & objRootDSE.Get("defaultNamingContext") &_
		"' WHERE objectClass='user' AND objectCategory='person'"
	Set objRootDSE = Nothing
 
	objCommand.Properties("Page Size") = 1000
	objCommand.Properties("Timeout") = 600
	objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE
	objCommand.Properties("Cache Results") = False
 
	Set objRecordSet = objCommand.Execute
 
	' Update all entries in the Dictionary with a Distinguished Name
 
	While Not objRecordSet.EOF
		strUsername = objRecordSet.Fields("sAMAccountName")
		strDN = objRecordSet.Fields("distinguishedName")
		If objUsers.Exists(strUsername) Then
			arrData = objUsers(strUsername)
			arrData(1) = strDN
 
			objUsers(strUsername) = arrData
		End If
		objRecordSet.MoveNext
	Wend
	objConnection.Close
 
	Set objRecordSet = Nothing
	Set objCommand = Nothing
	Set objConnection = Nothing
End Sub
 
Sub DisableAccounts
	' Check each account in the Exchange Domain is Disabled
 
	Const ADS_UF_ACCOUNTDISABLE = &H2
 
	Dim strUsername, strDN
	Dim objUser
	Dim intUAC
 
	For Each strUsername in objUsers
		strDN = objUsers(strUsername)(1)
 
		Set objUser = GetObject("LDAP://" & strDN)
		intUAC = objUser.Get("userAccountControl")
 
		' Look for the Account Disable Flag in userAccountControl
 
		If intUAC And ADS_UF_ACCOUNTDISABLE Then
			' Account is already Disabled
		Else
			' Disable the Account
			intUAC = intUAC XOr ADS_UF_ACCOUNTDISABLE
			objUser.Put "userAccountControl", intUAC
			objUser.SetInfo
		End If
		Set objUser = Nothing
	Next
End Sub
 
Function GetNetBIOSDomainName(strDomain)
	' Modified version of sample code from "Active Directory Cookbook" by Robbie Allen
 
	Dim objRootDSE, objConnection, objRecordSet
	Dim strADSPath, strFilter, strAttrs, strScope
 
	Set objRootDSE = GetObject("LDAP://" & strDomain & "/RootDSE")
 
	strADsPath =  "<LDAP://" & strDomain & "/CN=Partitions," & _
		objRootDSE.Get("configurationNamingContext") & ">;"
 
	strFilter = "(&(objectcategory=Crossref)" & _
		"(dnsRoot=" & strDomain & ")(netBIOSName=*));"
	strAttrs = "netbiosname;"
	strScope = "Onelevel"
 
	Set objConnection = CreateObject("ADODB.Connection")
	objConnection.Provider = "ADsDSOObject"
	objConnection.Open "Active Directory Provider"
 
	Set objRecordSet = objConnection.Execute(strADsPath & strFilter & strAttrs & strScope)
	objRecordSet.MoveFirst
 
	GetNetBIOSDomainName = objRecordSet.Fields("netbiosName")
 
	Set objRecordSet = Nothing
	Set objConnection = Nothing
End Function
 
Sub SetAEA
	' Add an Associated External Account Access Control Entry to the Access Control List
	' for each of the users we matched up.
 
	Const ADS_ACETYPE_ACCESS_ALLOWED = &H00000
	Const ADS_ACEFLAG_INHERIT_ACE = &H00002
 
	Const ADS_RIGHT_FULL_MB_ACCESS = &H00001
	Const ADS_RIGHT_ASSOCIATED_EXTERNAL_ACCOUNT = &H00004
 
	Dim objUser, objMailboxSD, objDACL, objACE
	Dim strExchangeUsername, strDN, strExternalUsername, strNetBIOSDomain
	Dim booFoundAEA
 
	strNetBIOSDomain = GetNetBIOSDomainName(EXTERNAL_DOMAIN)
 
	For Each strExchangeUsername in objUsers
		strDN = objUsers(strExchangeUsername)(1)
		strExternalUsername = objUsers(strExchangeUsername)(0)
 
		' Don't run this for Users we didn't manage to get the DistinguishedName for
 
		If strDN <> "" Then
			Set objUser = GetObject("LDAP://" & strDN)
	
			' Grab the Mailbox Access Control List from the User Account
 
			Set objMailboxSD = objUser.MailboxRights
			Set objDACL = objMailboxSD.DiscretionaryAcl
	
			booFoundAEA = False
 
			' Check the existing Access Control List. If we find as Associated External Account
			' matching the one we'd add we'll just skip it.
	
			For Each objACE in objDACL
				If objACE.AccessMask And ADS_RIGHT_ASSOCIATED_EXTERNAL_ACCOUNT Then
					If LCase(objACE.Trustee) <> _ 
							LCase(strNetBIOSDomain & "\" & strExternalUsername) Then
	
						' If it doesn't match the one we want we'll remove the current ACE
	
						objDACL.RemoveAce objACE
					Else
						booFoundAEA = True
					End If
				End If
			Next
			Set objACE = Nothing
 
			' Create a new Access Control Entry and add it to the DACL (if there's not one already)
 
			If booFoundAEA = False Then
				Set objACE = CreateObject("AccessControlEntry")
	
				objACE.Trustee = strNetBIOSDomain & "\" & strExternalUsername
				objACE.AccessMask = ADS_RIGHT_FULL_MB_ACCESS + _
					ADS_RIGHT_ASSOCIATED_EXTERNAL_ACCOUNT
				objACE.AceFlags = ADS_ACEFLAG_INHERIT_ACE
				objACE.AceType = ADS_ACETYPE_ACCESS_ALLOWED
 
				' If the SID exists in both Domains this will cause an error
				' Normally occurs if ADMT has been used to shift a user account 
				' while maintaining the SID (sidHistory).
	
				objDACL.AddAce(objACE)
				objMailboxSD.DiscretionaryAcl = objDACL
				objUser.MailboxRights = Array(objMailboxSD)
				objUser.SetInfo
			End If
	
			Set objACE = Nothing
			Set objMailboxSD = Nothing
			Set objDACL = Nothing
 
			Set objUser = Nothing
		End If
	Next
End Sub
 
'
' Main Code
'
 
Dim objUsers
 
' Create a Dictionary ("key, value" list)to hold the Users in
 
Set objUsers = CreateObject("Scripting.Dictionary")
 
ReadFile
GetADData
DisableAccounts
SetAEA
 
' Cleanup
 
Set objUsers = Nothing

Open in new window

0
 

Author Comment

by:ASAdmin
ID: 20840514
Fantastic,
I will test it out in my test lab today after making the necessary modifications.
does the csv file contain just two fields(ofcourse thats all it needs anyways) .
And the next thing is will the script also set the full mailbox access property as well?

Thank you so much.
0
Easily manage email signatures in Office 365

Managing email signatures in Office 365 can be a challenging task if you don't have the right tool. CodeTwo Email Signatures for Office 365 will help you implement a unified email signature look, no matter what email client is used by users. Test it for free!

 
LVL 71

Expert Comment

by:Chris Dent
ID: 20840534

Hey,

Yep, just those two fields.

And yes, it sets that. This is the bit that sets the Mask:

objACE.AccessMask = ADS_RIGHT_FULL_MB_ACCESS + _
      ADS_RIGHT_ASSOCIATED_EXTERNAL_ACCOUNT

And since Associated External isn't much good without mailbox access we have both :)

Chris
0
 

Author Comment

by:ASAdmin
ID: 20840566
Fantastic Chris,
I will test that today in my lab and then assign you points.

Than you
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20840589

No problem, yell if you have any problems with it. Did test it as much as I could here, the only problem I ran into was where the SID for the Associated External Account exists in both domains (if ADMT has been run and the SID is present in a SID history).

Chris
0
 

Author Comment

by:ASAdmin
ID: 20840596
One more quick question, Do we have to disable the account on which we are setting these properties?
I am asking this question from two perspectives

1. If we disable the account, does it recieve emails?
2. What if we need to login as that account in the old (Exchange domain)?

Thankyou
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20840647

No, you don't have to disable the source if you don't want to, sorry I forgot to note that was included.

Feel free to remove the call to the Subroutine DisableAccounts under Main Code (or remove that and the Subroutine that does it). It's intentionally modular so it'll happily survive without that step.

1. Yes provided that an Associated External Account is set
2. It won't let you :)

Chris
0
 

Author Comment

by:ASAdmin
ID: 20842190
Perfect, I'd rather remove that module.

Thank you.
0
 

Author Comment

by:ASAdmin
ID: 20849888
one more quick question, on the csv file, is there a header row or just the values through out( I am thinking it is the sAMAccountName that we are going to put in that field, correct me if I am wrong).

thank you
0
 

Author Comment

by:ASAdmin
ID: 20850021
Hi Chris,
I am getting the fallowing error trying to run that script.

Line:182
Char: 4
Object doesn't support this property or method, objuser.Mailboxrights
Code: 800A01B6

Regards
0
 

Author Comment

by:ASAdmin
ID: 20850084
Never mind, I ran it on the exchange server in the source and it worked smooth, no problems at all.,
0
 

Author Closing Comment

by:ASAdmin
ID: 31428574
Thank you Chris for helping me with this.
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20851530

Excellent :) Should have mentioned that it must run run from a system with the Exchange System Tools installed.

Chris
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 20851559

Missed a bit:

> is there a header row or just the values through out

Just the values, I didn't write anything in to account for a header row.

Chris
0
 

Expert Comment

by:moe9107
ID: 23654998
Hi,
I have tried this script but I get the following error.
"The specified directory service attribute or value does not exist" Line 221

I have migrated the user accounts from one forest to another while leaving Exchange in the source forest. The users are migrated with SID history as some resources are remaining in the source forest for the time being. I was wondering if the error is due to the SID history and whether there is a way to get the script modified to allow it to work in this situation.

Thanks in advance
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 23657733

Will it let you manually assign the permission using the GUI? If it does, then yes, there's a chance we can fix it. If it doesn't we're a bit stuck.

Chris
0
 

Expert Comment

by:MikeEdd
ID: 32631231
I too need to be able to preserve SID history during a ADMT migration. and I need to associate to a  external account(the old email account on the old domain) I can do this via the GUI, does this script work with SID History?

MikeEdd
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 32632905

As above, if you can do it in the GUI the script can deal with it. If you cannot then it won't be able to help.

Chris
0
 
LVL 15

Expert Comment

by:markpalinux
ID: 32793860
Thanks  - this helped me a ton.

It set the both things I needed.
Full mailbox Access
 Associated external account

Thank you,
Mark Leddy
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33671834
Hi there,

This is the best thing i have discovered since starting on a MAJOR domain migration. Thanks :)

I am also running into the SID history issue (Yes ADMT was used to migrate users)

To answer the question about if its possible in the GUI... yes, i can do all the script tasks via GUI.

(I am an absolute scripting idiot - so this is just a dangerous errant though)
Since SID history is the issue here, are there perhaps some of the mailbox permissions that dont need to be set? - hence avoiding the error?

I still have almost 10k users to migrate in the next 2 weeks - so i would LOVE to get this working.

Regards
Jacqueline

PS. i did add one thing to the script.
I added a section to update the mailNickName in AD (with the 'strExternalUsername') - its probably only in my environment that this is required.
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 33671855

> Since SID history is the issue here, are there perhaps some of the mailbox permissions that dont need to be set?
> - hence avoiding the error?

Not really I'm afraid. Existing permissions will be bound to SELF rather than an explicit entry for the SID.

Chris
0
 

Author Comment

by:ASAdmin
ID: 33672111
Hmm!!
When I created this question, I was using Quest migration tools for that perticular migraion. Later our company aquired another company(smaller one) and needed to merge that domain into ours and I used ADMT since our company didn't want spend the ransom on Quest, and the script still worked fine.
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33673095
@ ASAdmin

You probably used ADMT, but didnt carry the SID history with... i WISH our company went with that idea... instead they went the FULL microsoft idea... with all its issues.

let me go play with this some more :)
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 33673125

It was written to work alongside a migration based on ADMT, it should be fine :)

Chris
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33673399
I get the following error :
>>User-Mig.vbs(109, 3) (null): 0x80005000
line 109 in my scriptfile is: Set objUser = GetObject("LDAP://" & strDN)

But i only get this on accounts that were migrated with ADMT. my test accounts dont get this - they work fine.
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33679744
Ok... fixed it.
Replaced the "diableuser" sub with different code... .works fine :)

(dont ask my why of all things that didnt work.... my coding skills are just not good enough)

Thanks a million... this makes my target of 10k users in the next 2 weeks possible :)
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 33679884

Good luck Jacqueline :)

Chris
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33680683
I fixed it :)

Line 33: strExchangeUsername = LCase(arrLine(0))

Took out the  LCase statement... most my usernames have Caps in....

Now the script works (in its original form) accross the board.

i suppose i could have added another  LCase statement into line 82 If objUsers.Exists(strUsername) Then

the main thing, is that it now works on my domain.... (i didnt realise that my test accounts were all lowercase) until i stumbled onto the issue.
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 33680715

You can make objUsers case insensitive:


Set objUsers = CreateObject("Scripting.Dictionary")
objUsers.CompareMove = vbTextCompare


Otherwise LCase as you thought.

Chris
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 33680725

er should have been:


objUsers.CompareMode = vbTextCompare


Chris
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33680873
ERm... now i am going to sound like an idiot....

where do i add that in???? (i think i fried my brain already today... no more thinking power left)
0
 
LVL 71

Expert Comment

by:Chris Dent
ID: 33680930

Immediately after this line (243 in the original):

Set objUsers = CreateObject("Scripting.Dictionary")

That creates the dictionary object, by default it does binary comparison, effectively case sensitive comparison. By changing CompareMode you can make it ignore case, so you end up with this in place of the original:

Set objUsers = CreateObject("Scripting.Dictionary")
objUsers.CompareMode = VbTextCompare

Chris
0
 

Expert Comment

by:Jacqueline-Rathbone
ID: 33680986
Thanks a million :)
0

Featured Post

Take Control of Web Hosting For Your Clients

As a web developer or IT admin, successfully managing multiple client accounts can be challenging. In this webinar we will look at the tools provided by Media Temple and Plesk to make managing your clients’ hosting easier.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The article explains the process to deploy a Self-Service password reset portal I developed a few years ago. Hopefully, it will prove useful to someone.  Any comments, bug reports etc. are welcome...
This article explains how to move an Exchange 2013/2016 mailbox database and logs to a different drive.
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …
This video demonstrates how to sync Microsoft Exchange Public Folders with smartphones using CodeTwo Exchange Sync and Exchange ActiveSync. To learn more about CodeTwo Exchange Sync and download the free trial, go to: http://www.codetwo.com/excha…
Suggested Courses

611 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question