Solved

Problem creating distribution lists on exchange server 2010 via power shell

Posted on 2012-04-04
4
882 Views
Last Modified: 2012-04-16
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.Management.PowerShell.E2010")
* 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.Management.PowerShell.E2010 kann aufgrund des folgenden Fehlers nicht geladen werden: Der Typeninitialisierer für "Microsoft.Exchange.Data.Directory.Globals" hat eine Ausnahme verursacht.
   bei System.Management.Automation.Runspaces.RunspaceConfigForSingleShell.LoadCustomPSSnapIn(PSSnapInInfo mshsnapinInfo)
   bei System.Management.Automation.Runspaces.RunspaceConfigForSingleShell.LoadPSSnapIn(PSSnapInInfo mshsnapinInfo)
   bei System.Management.Automation.Runspaces.RunspaceConfigForSingleShell.LoadPSSnapIn(PSSnapInInfo mshsnapinInfo, PSSnapInException& warning)
   bei System.Management.Automation.Runspaces.RunspaceConfigForSingleShell.DoAddPSSnapIn(String name, PSSnapInException& warning)
   bei System.Management.Automation.Runspaces.RunspaceConfiguration.AddPSSnapIn(String name, PSSnapInException& warning)
   bei mpc.Utilities.PowerShellCommand.AddSnapIn(String 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.Directory.Globals" 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:

 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

Open in new window

0
Comment
Question by:srexp
  • 3
4 Comments
 
LVL 37

Expert Comment

by:Neil Russell
ID: 37806453
Your code for StartImpersonation(...) is all commented out. Is that just a typo in your posting??
0
 

Author Comment

by:srexp
ID: 37806491
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:

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

Open in new window

0
 

Accepted Solution

by:
srexp earned 0 total points
ID: 37832867
I am using Remote Power Shell now to avoid this problem.
0
 

Author Closing Comment

by:srexp
ID: 37850309
Not solved. Workaround does work.
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Resolve Outlook connectivity issues after moving mailbox to new Exchange 2016 server
Find out how to use Active Directory data for email signature management in Microsoft Exchange and Office 365.
In this video we show how to create a Shared Mailbox in Exchange 2013. We show this process by using the Exchange Admin Center. Log into Exchange Admin Center.: First we need to log into the Exchange Admin Center. Navigate to the Recipients >> Sha…
This video discusses moving either the default database or any database to a new volume.

746 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

Need Help in Real-Time?

Connect with top rated Experts

10 Experts available now in Live!

Get 1:1 Help Now