srexp
asked on
Problem creating distribution lists on exchange server 2010 via power shell
Hi there,
we have an it management system running where we can put users into special groups. Eg. every user who uses Microsoft Word is in that group. Furthermore this tool has a kind of task server that can run tasks periodically.
The idea now is, that we would like to create distribution lists on the exchange server with the mentioned groups, like "All Word Users".
The task servers has some requirements that cannot easily be changed:
* The TaskServer can run multiple tasks threaded simultaneously
* The TaskServer is a windows service that runs under a domain account with local admin rights.
* The TaskServer-User has no authorization to do anything on the exchange server
* It is possible to impersonate a task to run with appropriate rights.
That's the prehistory so far. The solution looks as follows:
* I have created this task.
* I have installed the exchange power shell commandlets
("Microsoft.Exchange.Manag ement.Powe rShell.E20 10")
* I have created a power shell command with runspace and pipeline
* I have impersonated the command with appropriate rights
The mean thing is now, that my code functions properly when
* I run the code without any service involved in a console application.
* It also runs as a windows service hosted in a console application.
* It does not run, when the service is hosted in a windows service (installutil etc.). I get the following error message:
Das Windows PowerShell-Snap-In Microsoft.Exchange.Managem ent.PowerS hell.E2010 kann aufgrund des folgenden Fehlers nicht geladen werden: Der Typeninitialisierer für "Microsoft.Exchange.Data.D irectory.G lobals" hat eine Ausnahme verursacht.
bei System.Management.Automati on.Runspac es.Runspac eConfigFor SingleShel l.LoadCust omPSSnapIn (PSSnapInI nfo mshsnapinInfo)
bei System.Management.Automati on.Runspac es.Runspac eConfigFor SingleShel l.LoadPSSn apIn(PSSna pInInfo mshsnapinInfo)
bei System.Management.Automati on.Runspac es.Runspac eConfigFor SingleShel l.LoadPSSn apIn(PSSna pInInfo mshsnapinInfo, PSSnapInException& warning)
bei System.Management.Automati on.Runspac es.Runspac eConfigFor SingleShel l.DoAddPSS napIn(Stri ng name, PSSnapInException& warning)
bei System.Management.Automati on.Runspac es.Runspac eConfigura tion.AddPS SnapIn(Str ing name, PSSnapInException& warning)
bei mpc.Utilities.PowerShellCo mmand.AddS napIn(Stri ng psSnapInName) in E:\Visual Studio
--> Short Translation: "The Windows PowerShell-SnapIn Microsoft.Exchange.... could not be loaded due to an error. The Type Initialization "Microsoft.Exchange.Data.D irectory.G lobals" has thrown an excetion at ...."
I have already checked a possible x86/x64 issue. Both the service as well as the task are compiled with "Any CPU".
Any idea would be more then welcomed.
Thanks.
Here is my sourcecode:
we have an it management system running where we can put users into special groups. Eg. every user who uses Microsoft Word is in that group. Furthermore this tool has a kind of task server that can run tasks periodically.
The idea now is, that we would like to create distribution lists on the exchange server with the mentioned groups, like "All Word Users".
The task servers has some requirements that cannot easily be changed:
* The TaskServer can run multiple tasks threaded simultaneously
* The TaskServer is a windows service that runs under a domain account with local admin rights.
* The TaskServer-User has no authorization to do anything on the exchange server
* It is possible to impersonate a task to run with appropriate rights.
That's the prehistory so far. The solution looks as follows:
* I have created this task.
* I have installed the exchange power shell commandlets
("Microsoft.Exchange.Manag
* I have created a power shell command with runspace and pipeline
* I have impersonated the command with appropriate rights
The mean thing is now, that my code functions properly when
* I run the code without any service involved in a console application.
* It also runs as a windows service hosted in a console application.
* It does not run, when the service is hosted in a windows service (installutil etc.). I get the following error message:
Das Windows PowerShell-Snap-In Microsoft.Exchange.Managem
bei System.Management.Automati
bei System.Management.Automati
bei System.Management.Automati
bei System.Management.Automati
bei System.Management.Automati
bei mpc.Utilities.PowerShellCo
--> Short Translation: "The Windows PowerShell-SnapIn Microsoft.Exchange.... could not be loaded due to an error. The Type Initialization "Microsoft.Exchange.Data.D
I have already checked a possible x86/x64 issue. Both the service as well as the task are compiled with "Any CPU".
Any idea would be more then welcomed.
Thanks.
Here is my sourcecode:
Private Sub CreateExchangeDistributionList(ByVal name As String, ByVal organizationalUnit As String, ByVal samAccountName As String)
'StartImpersonation(ModuleLibrarySharedConfig.GetConfigValue(BindableConfiguration.StandardParameterNamesEnum.ImpersonationUserName),
' ModuleLibrarySharedConfig.GetConfigValue(BindableConfiguration.StandardParameterNamesEnum.ImpersonationUserDomain),
' ModuleLibrarySharedConfig.GetConfigValue(BindableConfiguration.StandardParameterNamesEnum.ImpersonationUserPassword))
Dim cmd As String = String.Format("New-DistributionGroup -Name ""{0}"" -OrganizationalUnit ""{1}"" -SamAccountName ""{2}"" -Type ""Distribution""",
name,
organizationalUnit,
samAccountName)
Dim ps As New PowerShellCommand
ps.AddSnapIn("Microsoft.Exchange.Management.PowerShell.E2010")
ps.AddCommand(cmd)
ps.Run()
EndImpersonation()
If ps.HasError Then
Throw New Exception(ps.GetLog)
End If
End Sub
Public Class PowerShellCommand
' //// EVENTS
' //// DEKLARATIONEN
Private _log As String
Private _hasErrors As Boolean
Private ReadOnly _runspace As Runspace
Private ReadOnly _pipeline As Pipeline
' //// PROPERTIES
Public ReadOnly Property HasError As Boolean
Get
Return _hasErrors
End Get
End Property
Public ReadOnly Property GetLog As String
Get
Return _log
End Get
End Property
' //// EVENTHANDLER
' //// METHODEN
Public Sub AddSnapIn(ByVal psSnapInName As String)
' Exchange Snapin
' "Microsoft.Exchange.Management.PowerShell.E2010"
Dim snapInWarning As New PSSnapInException
Dim snapInInfo = _runspace.RunspaceConfiguration.AddPSSnapIn(psSnapInName, snapInWarning)
snapInInfo.LogPipelineExecutionDetails = True
If snapInWarning IsNot Nothing Then
Throw New Exception("Power Shell SnapIn Warning", snapInWarning)
End If
End Sub
Public Sub AddCommand(ByVal commandText As String)
' "New-DistributionGroup -Name ""Mail_Abwesenheitsplaner_U"" -OrganizationalUnit ""mpc-group.com/Firmen/Groups/Mail"" -SamAccountName ""Mail_Abwesenheitsplaner_U"" -Type ""Distribution"""
_pipeline.Commands.Add(New Command(commandText, True))
End Sub
' //// INIT
Public Sub New()
Dim runspaceConfig = RunspaceConfiguration.Create()
_runspace = RunspaceFactory.CreateRunspace(runspaceConfig)
_pipeline = _runspace.CreatePipeline
End Sub
' //// ENUMS
' //// INLINE KLASSEN
Public Sub Run()
Try
ClearLog()
AddLogEntry("PowerShell - Execution started at " & Now.ToString)
AddLogEntry("Current User: " & System.Environment.UserName)
_runspace.Open()
Dim out = _pipeline.Invoke()
Dim builder As New System.Text.StringBuilder
Dim info = _pipeline.PipelineStateInfo
If (info.State <> PipelineState.Completed) Then
AddLogEntry("---PipelineState: " & info.State.ToString)
AddLogEntry(info.Reason.ToString)
End If
If _pipeline.Error.Count > 0 Then
LogError(_pipeline.Error)
End If
Catch ex As Exception
LogError(ex)
End Try
If _hasErrors Then
FlushLogToEventLog(_log)
End If
End Sub
Private Sub ClearLog()
_log = ""
_hasErrors = False
End Sub
Private Sub AddLogEntry(ByVal entry As String)
_log &= entry & Environment.NewLine
End Sub
Private Sub LogError(ByVal pipelineError As PipelineReader(Of Object))
Dim x As Integer
Dim log As New System.Text.StringBuilder
If pipelineError.Count > 0 Then
While Not pipelineError.EndOfPipeline
log.AppendFormat("---- Error from pipeline [{0}]", x)
Dim value = TryCast(pipelineError.Read(), PSObject)
If value IsNot Nothing Then
'get the ErrorRecord
Dim r = TryCast(value.BaseObject, ErrorRecord)
If r IsNot Nothing Then
'build whatever kind of message your want
log.AppendLine(Convert.ToString(r.InvocationInfo.MyCommand.Name) & " : " & Convert.ToString(r.Exception.Message))
log.AppendLine(r.InvocationInfo.PositionMessage)
log.AppendLine(String.Format("+ CategoryInfo: {0}", r.CategoryInfo))
log.AppendLine(String.Format("+ FullyQualifiedErrorId: {0}", r.FullyQualifiedErrorId))
End If
End If
End While
End If
LogError(log.ToString)
End Sub
Private Sub LogError(ByVal ex As Exception)
LogError(ex.Message)
If ex.InnerException IsNot Nothing Then
LogError(ex.InnerException)
End If
End Sub
Private Sub LogError(ByVal err As String)
AddLogEntry(err)
_hasErrors = True
End Sub
Public Shared Sub FlushLogToEventLog(ByVal log As String)
Dim sSource As String
Dim sLog As String
Dim sEvent As String
Dim sMachine As String
sSource = "PowerShell Log"
sLog = "Application"
sEvent = log
sMachine = "."
If Not EventLog.SourceExists(sSource, sMachine) Then
EventLog.CreateEventSource(sSource, sLog, sMachine)
End If
Dim eLog As New EventLog(sLog, sMachine, sSource)
eLog.WriteEntry(sEvent, EventLogEntryType.Error, 234, CType(3, Short))
End Sub
End Class
Your code for StartImpersonation(...) is all commented out. Is that just a typo in your posting??
ASKER
That's a typo in my posting. This should not be commented out. The error occurs with impersonation to.
And as I said. The TaskServer User does not have rights to create lists on the exchange server. The code that gets executed in the three metioned scenarios is the same. Tow are working one not.
Here is the code I'm using for the impersonation:
And as I said. The TaskServer User does not have rights to create lists on the exchange server. The code that gets executed in the three metioned scenarios is the same. Tow are working one not.
Here is the code I'm using for the impersonation:
Private Sub ImpersonateValidUser(ByVal userName As String, ByVal domain As String, ByVal password As String)
ImpersonateValidUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE)
End Sub
Private Sub ImpersonateValidUser(ByVal userName As String, ByVal domain As String, ByVal password As String, ByVal provider As Integer)
Dim tempWindowsIdentity As WindowsIdentity = Nothing
Dim token As IntPtr = IntPtr.Zero
Dim tokenDuplicate As IntPtr = IntPtr.Zero
Try
If RevertToSelf() Then
If LogonUser(userName, domain, password, provider, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
impersonationContext = tempWindowsIdentity.Impersonate()
Else
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Else
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Else
Throw New Win32Exception(Marshal.GetLastWin32Error())
End If
Finally
If token <> IntPtr.Zero Then
CloseHandle(token)
End If
If tokenDuplicate <> IntPtr.Zero Then
CloseHandle(tokenDuplicate)
End If
End Try
End Sub
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Not solved. Workaround does work.