Hobby Chef
This is my first attempt to write a Powershell sample to my readers. In this sample we will give Send-As, Receive-As, ms-Exch-Store-Admin rights to an active directory user. We will be piping two commands here… Get-MailboxServer and the result will go to Add-AdPermission.
You may give same permissions using Exchange Power Shell Window
Here is the sample…
using System; using System.Collections.Generic; using System.Text; using System.Management.Automation.Runspaces; using System.Collections.ObjectModel; namespace PowershellSample { class Program { static void Main(string[] args) { RunspaceConfiguration config = RunspaceConfiguration.Create(); PSSnapInException warning; // Load Exchange PowerShell snap-in. config.AddPSSnapIn("Microsoft.Exchange.Management.PowerShell.Admin", out warning); if (warning != null) throw warning; using (Runspace thisRunspace = RunspaceFactory.CreateRunspace(config)) { try { thisRunspace.Open(); using (Pipeline thisPipeline = thisRunspace.CreatePipeline()) { //Please change parameter values. Command cmdGetMailbox = new Command("Get-MailboxServer"); Command cmdAddAdPermission = new Command("Add-AdPermission"); cmdGetMailbox.Parameters.Add("Identity", @"MSGEX07"); cmdAddAdPermission.Parameters.Add("User", "UserA"); cmdAddAdPermission.Parameters.Add("AccessRights", new string[] { "GenericRead", "GenericWrite" }); cmdAddAdPermission.Parameters.Add("ExtendedRights", new string[] { "Send-As", "Receive-As", "ms-Exch-Store-Admin" }); thisPipeline.Commands.Add(cmdGetMailbox); thisPipeline.Commands.Add(cmdAddAdPermission); try { thisPipeline.Invoke(); } catch (Exception exx) { Console.Write("Error: " + exx.ToString()); } // Check for errors in the pipeline and throw an exception if necessary. if (thisPipeline.Error != null && thisPipeline.Error.Count > 0) { StringBuilder pipelineError = new StringBuilder(); pipelineError.AppendFormat("Error calling cmdLets..."); foreach (object item in thisPipeline.Error.ReadToEnd()) { pipelineError.AppendFormat("{0}\n", item.ToString()); } throw new Exception(pipelineError.ToString()); } } } finally { thisRunspace.Close(); } } } } }
I have seen lots of confusion lately and people asking this question in various forums. I do not have an official statement yet on this and waiting for a response from Exchange SDK documentation owners.
Here is my understanding, I may be wrong here.
According to http://www.microsoft.com/downloadS/details.aspx?familyid=47720D33-FA5A-4AF4-A8EB-FA39695CBAD1&displaylang=en, the Exchange 2007 SP1 SDK does not list Windows 2008 as supported platform.
It does not says it’s NOT supported but it also does not mention that whether its supported or not. Usually something which is NOT documented as “not supported” is assumed to be supported :-)
Let’s take an example of “Exchange 2007 SP1” - http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=44c66ad6-f185-4a1d-a9ab-473c1188954c
The download page does not mention that it’s supported on Windows 2008 while it is the only version of Exchange so far which is supported on Windows 2008
Source: http://technet.microsoft.com/en-us/library/bb232170.aspx
Exchange 2007 and Windows Server 2008
The RTM version of Exchange 2007 cannot be installed on Windows Server 2008. However, Exchange 2007 SP1 is supported for installation on Windows Server 2008. Although you cannot install the RTM version of Exchange 2007 on Windows Server 2008, the RTM version of Exchange 2007 will be supported for use with Windows Server 2008 directory servers.
Exchange Server 2003 Service Pack 2 (SP2) is similarly supported. Exchange Server 2003 cannot be installed on a computer running Windows Server 2008, but Exchange 2003 will be supported for use with Windows Server 2008 directory servers.
Be aware of the following when combining Microsoft Exchange with Windows Server 2008:
With all that into consideration, it is safe to assume that if you are running with Exchange Sp1 Management Tools on a Windows 2008 machine you are under support boundaries to install and use SDK. I will update this document once I have more information along with facts & confirmation from documentation team.
Stay tuned…
EDIT: UPDATED ON 3/18/2009
I have got confirmation from product group that its a document bug and nothing more or less. So we DO support Exchange 2007 SP1 SDK on Windows Server 2008.
If you ever tried to modify the msExchMailboxSecurityDescriptor attribute directory then you must be aware that this attribute exists on the user object in the Active Directory and stores a partial copy of the user's mailbox security descriptor. This attribute is not back linked to the user's mailbox security descriptor.
If msExchMailboxSecurityDescriptor is modified directly, the actual mailbox security descriptor on the user's mailbox in the information store is not updated if the mailbox is already created. You can modify this attribute before mailbox is created and those permissions will retain even after the mailbox is created.
To be able to modify this attribute for existing mailboxes you need to use CDOEXM and run the script on Exchange 2003 server itself. If the user's mailbox security descriptor is modified from ADUnC or as demonstrated in the code in this article, msExchMailboxSecurityDescriptor is automatically updated to reflect these changes
Here is the code that I have used in a recent support case to add Associated External Account (AEA) permission. AEA permission require you to also assign Full Mailbox permission. This script will add both ACE to the mailbox.
Option Explicit 'Constant variable declarations CONST ADS_ACETYPE_ACCESS_ALLOWED = 0 CONST ADS_ACETYPE_ACCESS_DENIED = 1 CONST ADS_ACETYPE_SYSTEM_AUDIT = 2 CONST ADS_ACETYPE_ACCESS_ALLOWED_OBJECT = 5 CONST ADS_ACETYPE_ACCESS_DENIED_OBJECT = 6 CONST ADS_ACETYPE_SYSTEM_AUDIT_OBJECT = 7 CONST ADS_ACETYPE_SYSTEM_ALARM_OBJECT = 8 CONST ADS_RIGHT_DS_CREATE_CHILD = 1 CONST ADS_ACEFLAG_INHERIT_ACE = 2 'Used for Associate external account Const E2K_MB_FULL_MB_ACCESS = &H1 Const E2K_MB_SEND_AS = &H2 Const E2K_MB_EXTERNAL_ACCOUNT = &H4 Const E2K_MB_READ_PERMISSIONS = &H20000 Const E2K_MB_TAKE_OWNERSHIP = &H80000 ' ******************************************************************** ' Change these variables according to your environment. Const sUserADsPath = "LDAP://SERVER/CN=USER,CN=Users,DC=ExchangeLab,DC=com" Const sTrustee = "NT AUTHORITY\SELF" ' ******************************************************************** Dim objUser Dim oSecurityDescriptor Dim dacl Dim ace Dim bFound bFound = False 'Get directory user object. Set objUser = GetObject(sUserADsPath) ' Get the Mailbox security descriptor (SD). Set oSecurityDescriptor = objUser.MailboxRights ' Extract the Discretionary Access Control List (DACL) using the IADsSecurityDescriptor. ' Interface. Set dacl = oSecurityDescriptor.DiscretionaryAcl Set ace = CreateObject("AccessControlEntry") AddAce dacl, sTrustee, E2K_MB_FULL_MB_ACCESS Or E2K_MB_EXTERNAL_ACCOUNT, ADS_ACETYPE_ACCESS_ALLOWED, ADS_ACEFLAG_INHERIT_ACE, 0, 0, 0 ' Add the modified DACL to the security descriptor. oSecurityDescriptor.DiscretionaryAcl = dacl ' Save new SD onto the user. objUser.MailboxRights = oSecurityDescriptor ' Commit changes from the property cache to the information store. objUser.SetInfo wscript.echo "Done modifying the mailboxsecurity descriptor" '******************************************************************** '* '* Function AddAce(dacl, TrusteeName, gAccessMask, gAceType, '* gAceFlags, gFlags, gObjectType, gInheritedObjectType) '* '* Purpose: Adds an ACE to a DACL '* Input: dacl Object's Discretionary Access Control List '* TrusteeName SID or Name of the trustee user account '* gAccessMask Access Permissions '* gAceType ACE Types '* gAceFlags Inherit ACEs from the owner of the ACL '* gFlags ACE has an object type or inherited object type '* gObjectType Used for Extended Rights '* gInheritedObjectType '* '* Output: Object - New DACL with the ACE added '* '******************************************************************** Function AddAce(dacl, TrusteeName, gAccessMask, gAceType, gAceFlags, gFlags, gObjectType, gInheritedObjectType) Dim Ace1 ' Create a new ACE object. Set Ace1 = CreateObject("AccessControlEntry") Ace1.AccessMask = gAccessMask Ace1.AceType = gAceType Ace1.AceFlags = gAceFlags Ace1.Flags = gFlags Ace1.Trustee = TrusteeName 'See whether ObjectType must be set If CStr(gObjectType) <> "0" Then Ace1.ObjectType = gObjectType End If 'See whether InheritedObjectType must be set. If CStr(gInheritedObjectType) <> "0" Then Ace1.InheritedObjectType = gInheritedObjectType End If dacl.AddAce Ace1 ' Destroy objects. Set Ace1 = Nothing End Function
Known Limitations: This script will throw an error if the same permissions (Associated External Mailbox) already exist on the mailbox.
I think you may find it useful as well. I wrote this script to monitor a particular mailbox for unread emails count in a particular folder and notify people about it. It could be useful to monitor a service mailbox and make sure it does not have any email unprocessed after a certain period of time. You may schedule this script as job to run every hour or so and notify you if there were any unread emails pending in the inbox.
Const strServer = "ExchangeServerName" ' Exchange 2003 Server's HOSTNAME/IPADDRESS Const strDomain = "ExchangeLab" ' Credentials to login to mailbox Const strUser = "Administrator" ' It could be credentials for the same mailbox Const strPass = "********" ' or the credentials of a service account which has access to all mailboxes Const strMailBox = "Administrator" ' Mailbox alias to login to Const strFrom = "administrator@exchangelab.com" ' From address of sender, ideally should be same as the Mailbox Const strTo = "administrator@exchangelab.com" ' Email sent TO Const strSubject = "Unread mails alert!" ' Email's subject Const strBody = "You have {X} unread emails" ' leave the {X} as is and this will be replaced with the actual number of unread message Const SSL_Enabled = "" ' change this to "s" if you want to enable SSL (HTTPS) Const MaxUnreadAllowed = 10 ' change it to maximum unread allowed in an inbox Const strLogFilePath = "C:\UnreadEmailMonitor.log" ' Change to the file path where you want to save the log Dim xDoc Dim xNodes Dim strURL Dim strResponse Dim HttpWebRequest Dim strXMLRequest Dim strUnreadCount strXMLRequest = "<?xml version=""1.0""?>" strXMLRequest = strXMLRequest + "<D:propfind xmlns:D=""DAV:"" xmlns:z=""urn:schemas:httpmail:"">" strXMLRequest = strXMLRequest + "<D:prop><z:unreadcount/></D:prop>" strXMLRequest = strXMLRequest + "</D:propfind>" strURL = "http" & SSL_Enabled & "://" & strServer & "/Exchange/" & strMailBox & "/Inbox" Set HttpWebRequest = CreateObject("microsoft.xmlhttp") Set xDoc = CreateObject("MSXML.DOMDocument") HttpWebRequest.open "PROPFIND", strURL, False, strUser, strPass HttpWebRequest.setRequestHeader "Content-type:", "text/xml" HttpWebRequest.send strXMLRequest WriteLog "Polling unread count from mailbox : " & strMailBox If (HttpWebRequest.Status >= 200 And HttpWebRequest.Status < 300) Then WriteLog "Success downloading mailbox data" ElseIf HttpWebRequest.Status = 401 Then WriteLog "You don't have permission to connect to this mailbox: " & strMailBox Else WriteLog "Error downloading mailbox data. Status: " & HttpWebRequest.Status & ": " & HttpWebRequest.statusText End If strResponse = HttpWebRequest.responseText If xDoc.loadXML(strResponse) Then Dim startPos Dim endPos Dim ns endPos = InStr(strResponse, "=""urn:schemas:httpmail:") startPos = InStrRev(strResponse, "xmlns:", endPos) + 6 ns = Mid(strResponse, startPos, endPos - startPos) strUnreadCount = xDoc.selectSingleNode("//" & ns & ":unreadcount").Text End If If strUnreadCount > MaxUnreadAllowed Then WriteLog "Unread emails found: " & strUnreadCount & " , sending mail to " & strTo strURL = "http" & SSL_Enabled & "://" & strServer & "/Exchange/" & strUser & "/##DavMailSubmissionURI##" HttpWebRequest.open "PUT", strURL, False, strDomain & "\" & strUser, strPass strXMLRequest = "From: " & strFrom & vbNewLine & _ "To: " & strTo & vbNewLine & _ "Subject: " & strSubject & vbNewLine & _ "Date: " & Now() & vbNewLine & _ "X-Mailer: Mailbox Polling Application" & vbNewLine & _ "MIME-Version: 1.0" & vbNewLine & _ "Content-Type: text/html" & vbNewLine & _ "Charset = ""iso-8859-1""" & vbNewLine & _ "Content-Transfer-Encoding: 7bit" & vbNewLine & vbNewLine & _ Replace(strBody, "{X}", strUnreadCount) HttpWebRequest.setRequestHeader "Translate", "f" HttpWebRequest.setRequestHeader "Content-Type", "message/rfc822" HttpWebRequest.setRequestHeader "Content-Length", "" & Len(strXMLRequest) HttpWebRequest.send strXMLRequest If (HttpWebRequest.Status >= 200 And HttpWebRequest.Status < 300) Then WriteLog "Message successfully sent." ElseIf HttpWebRequest.Status = 401 Then WriteLog "You don't have permission to send the Message." Else WriteLog "Message not successfully sent. Status: " & HttpWebRequest.Status & ": " & HttpWebRequest.statusText End If End If Private Sub WriteLog(ByVal sText) Dim objFSO Dim objTextFile Const ForAppending = 8 Set objFSO = CreateObject("Scripting.FileSystemObject") Set objTextFile = objFSO.OpenTextFile(strLogFilePath, ForAppending, True) ' Write a line. objTextFile.Write (sText & vbCRLFde) objTextFile.Close 'objTextFile.Close End Sub