0

我的最终目标是让下面的代码找到并针对域中的所有用户运行,而无需手动输入每个用户,但我不知道如何。然后我会将脚本放入任务调度程序中。

以下是这背后的故事和信息,以防对其他人有所帮助。

我的首席运营官希望我们的全球通讯录能够离线显示在每个人的设备(即 Android、IOS、Windows 等)中。我找到了一种方法,在 Exchange Powershell 中使用 Steve Goodmans 方法/代码一次一个用户进行操作 --> http://www.stevieg.org/2012/02/importing-global-address-list-entries-into -a-users-contacts-文件夹。对我自己的用户名执行此操作后,我可以离线查看手机上的 GAL 信息。而且,如果有人用他们的电话给我打电话,它现在会通过从导入的 GAL 中拉出,在我的手机上显示他们的名字。

基本上,使用他的代码,我将全局地址簿作为单独的地址簿复制给每个人。如果您在 Outlook 中查看您的联系人,您将看到名为 OrgContacts 的新通讯簿(在本例中)。当您的设备下次同步进行交换时,此地址簿也会同步,您将拥有整个公司。我们有几百个用户,所以这没什么大不了的。

到目前为止,我使用的代码一次只有一个用户。我需要帮助使它找到所有用户名并执行。我在运行字符串中尝试了通配符,但没有奏效。如果有的话,我也愿意采用一种完全不同的方式来实现这一点。

非常感谢您的宝贵时间,

要为每个用户运行代码,我使用这个...

# example (Billy Smith network username is basmith)
.\Copy-OrgContactsToUserContacts.ps1 -Mailbox basmith -FolderName OrgContacts

这是 Exchange 电源外壳代码...

param([string]$Mailbox,[string]$FolderName="OrgContacts");
#
# Copy-OrgContactsToUserMailboxContacts.ps1
#
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#
# Parameters
#  Mandatory:
# -MailboxFolder : Folder to "own" for these contacts.
#
# Creates s OrgContacts folder in the Mailbox and adds the contacts into it. Does not attempt to 


$EwsUrl = ([array](Get-WebServicesVirtualDirectory))[0].InternalURL.AbsoluteURI

$ContactMapping=@{
    "FirstName" = "GivenName";
    "LastName" = "Surname";
    "Company" = "CompanyName";
    "Department" = "Department";
    "Title" = "JobTitle";
    "WindowsEmailAddress" = "Email:EmailAddress1";
    "Phone" = "Phone:BusinessPhone";
    "MobilePhone" = "Phone:MobilePhone";
}

$UserMailbox  = Get-Mailbox $Mailbox

if (!$UserMailbox)
{
    throw "Mailbox $($Mailbox) not found";
    exit;
}

$EmailAddress = $UserMailbox.PrimarySMTPAddress

# Load EWS Managed API
[void][Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll");

$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
$service.UseDefaultCredentials = $true;
$service.URL = New-Object Uri($EwsUrl);

# Search for an existing copy of the Folder to store Org contacts 
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$RootFolder.Load()

$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where {$_.DisplayName -eq $FolderName}
if ($ContactsFolderSearch)
{
    # Empty if found
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
    $ContactsFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true)
} else {

    # Create new contacts folder
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
    $ContactsFolder = New-Object Microsoft.Exchange.WebServices.Data.ContactsFolder($service);
    $ContactsFolder.DisplayName = $FolderName
    $ContactsFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
    # Search for the new folder instance
    $RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
    $RootFolder.Load()
    $FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
    $ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where {$_.DisplayName -eq $FolderName}
    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
}

# Add contacts
$Users = get-user -Filter {WindowsEmailAddress -ne $null -and (MobilePhone -ne $null -or Phone -ne $null) -and WindowsEmailAddress -ne $EmailAddress} 
$Users = $Users | select DisplayName,FirstName,LastName,Title,Company,Department,WindowsEmailAddress,Phone,MobilePhone

foreach ($ContactItem in $Users)
{
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);

    $ExchangeContact = New-Object Microsoft.Exchange.WebServices.Data.Contact($service);
    if ($ContactItem.FirstName -and $ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.FirstName + " " + $ContactItem.LastName;
    }
    elseif ($ContactItem.FirstName -and !$ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.FirstName;
    }
    elseif (!$ContactItem.FirstName -and $ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.LastName;
    }
    elseif (!$ContactItem.FirstName -and !$ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.DisplayName;
        $ContactItem.FirstName = $ContactItem.DisplayName;
    }

    $ExchangeContact.DisplayName = $ExchangeContact.NickName;
    $ExchangeContact.FileAs = $ExchangeContact.NickName;

    # This uses the Contact Mapping above to save coding each and every field, one by one. Instead we look for a mapping and perform an action on
    # what maps across. As some methods need more "code" a fake multi-dimensional array (seperated by :'s) is used where needed.
    foreach ($Key in $ContactMapping.Keys)
    {
        # Only do something if the key exists
        if ($ContactItem.$Key)
        {
            # Will this call a more complicated mapping?
            if ($ContactMapping[$Key] -like "*:*")
            {
                # Make an array using the : to split items.
                $MappingArray = $ContactMapping[$Key].Split(":")
                # Do action
                switch ($MappingArray[0])
                {
                    "Email"
                    {
                        $ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::($MappingArray[1])] = $ContactItem.$Key.ToString();
                    }
                    "Phone"
                    {
                        $ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::($MappingArray[1])] = $ContactItem.$Key;
                    }
                }                
            } else {
                $ExchangeContact.($ContactMapping[$Key]) = $ContactItem.$Key;            
            }

        }    
    }
    # Save the contact    
    $ExchangeContact.Save($ContactsFolder.Id);

    # Provide output that can be used on the pipeline
    $Output_Object = New-Object Object;
    $Output_Object | Add-Member NoteProperty FileAs $ExchangeContact.FileAs;
    $Output_Object | Add-Member NoteProperty GivenName $ExchangeContact.GivenName;
    $Output_Object | Add-Member NoteProperty Surname $ExchangeContact.Surname;
    $Output_Object | Add-Member NoteProperty EmailAddress1 $ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1]
    $Output_Object;
}
4

2 回答 2

0

到目前为止,我使用的代码一次只有一个用户。我需要帮助使它找到所有用户名并执行。我在运行字符串中尝试了通配符,但没有奏效。如果有的话,我也愿意采用一种完全不同的方式来实现这一点。

您只是想输入 Exchange 组织中邮箱的名称吗?如果是这种情况,那么只需使用 Get-Mailbox 例如

Get-Mailbox -ResultSize Unlimited | foreach-object{
          $UserName = $_.SamAccountName

 }

将您的其他代码放入函数(或 cmdlet)并为每个邮箱调用该函数

如果要在计划任务中使用 Exchange 命令行管理程序 cmdlet,则需要执行类似http://mikepfeiffer.net/2010/02/creating-scheduled-tasks-for-exchange-2010-powershell-scripts/之类的操作

干杯格伦

于 2015-08-14T04:42:39.070 回答
0

首先,格伦,谢谢你给我链接并让我思考这个问题。你可能已经把它全部拼出来了,但我在这个过程中学到了一些东西。

使用风险自负!我不是程序员。我不会只在生产机器上这样做,但这取决于你。这会将所有联系人复制到所有用户。下次他们的手机或设备同步时,他们将在离线时拥有所有可用的联系人。它也将在他们的 Outlook 离线联系人中可用。我们只有 150 人。我不认为我会建议更多。它只为我们运行需要一个多小时。可能有一种方法可以构建 GAL,然后将其复制给每个用户,而不是像这样一遍又一遍地构建 GAL 的副本。但是,我对此知之甚少,无法做到这一点。

我让它工作。这不是优雅的方式,但它有效。

我将一步一步地放在这里,以防有人想要构建它。您唯一需要更改的是下面 ForEach 循环中的域名或 OU(第二个代码块)。

首先,我将此代码(第一个代码块)保存为来自 Steve Goodmans 方法/代码的 Copy-OrgContactsToUserContacts.ps1 --> http://www.stevieg.org/2012/02/importing-global-address-list-entries-into -a-users-contacts-文件夹

param([string]$Mailbox,[string]$FolderName="OrgContacts");
#
# Copy-OrgContactsToUserMailboxContacts.ps1
#
# THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE
# RISK OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.
#
# Parameters
#  Mandatory:
# -MailboxFolder : Folder to "own" for these contacts.
#
# Creates s OrgContacts folder in the Mailbox and adds the contacts into it. Does not attempt to 


$EwsUrl = ([array](Get-WebServicesVirtualDirectory))[0].InternalURL.AbsoluteURI

$ContactMapping=@{
    "FirstName" = "GivenName";
    "LastName" = "Surname";
    "Company" = "CompanyName";
    "Department" = "Department";
    "Title" = "JobTitle";
    "WindowsEmailAddress" = "Email:EmailAddress1";
    "Phone" = "Phone:BusinessPhone";
    "MobilePhone" = "Phone:MobilePhone";
}

$UserMailbox  = Get-Mailbox $Mailbox

if (!$UserMailbox)
{
    throw "Mailbox $($Mailbox) not found";
    exit;
}

$EmailAddress = $UserMailbox.PrimarySMTPAddress

# Load EWS Managed API
[void][Reflection.Assembly]::LoadFile("C:\Program Files\Microsoft\Exchange\Web Services\1.2\Microsoft.Exchange.WebServices.dll");

$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP1)
$service.UseDefaultCredentials = $true;
$service.URL = New-Object Uri($EwsUrl);

# Search for an existing copy of the Folder to store Org contacts 
$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
$RootFolder.Load()

$service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
$FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
$ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where {$_.DisplayName -eq $FolderName}
if ($ContactsFolderSearch)
{
    # Empty if found
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
    $ContactsFolder.Empty([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete, $true)
} else {

    # Create new contacts folder
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);
    $ContactsFolder = New-Object Microsoft.Exchange.WebServices.Data.ContactsFolder($service);
    $ContactsFolder.DisplayName = $FolderName
    $ContactsFolder.Save([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
    # Search for the new folder instance
    $RootFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,[Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot)
    $RootFolder.Load()
    $FolderView = new-Object Microsoft.Exchange.WebServices.Data.FolderView(1000)
    $ContactsFolderSearch = $RootFolder.FindFolders($FolderView) | Where {$_.DisplayName -eq $FolderName}
    $ContactsFolder = [Microsoft.Exchange.WebServices.Data.ContactsFolder]::Bind($service,$ContactsFolderSearch.Id);
}

# Add contacts
$Users = get-user -Filter {WindowsEmailAddress -ne $null -and (MobilePhone -ne $null -or Phone -ne $null) -and WindowsEmailAddress -ne $EmailAddress} 
$Users = $Users | select DisplayName,FirstName,LastName,Title,Company,Department,WindowsEmailAddress,Phone,MobilePhone

foreach ($ContactItem in $Users)
{
    $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $EmailAddress);

    $ExchangeContact = New-Object Microsoft.Exchange.WebServices.Data.Contact($service);
    if ($ContactItem.FirstName -and $ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.FirstName + " " + $ContactItem.LastName;
    }
    elseif ($ContactItem.FirstName -and !$ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.FirstName;
    }
    elseif (!$ContactItem.FirstName -and $ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.LastName;
    }
    elseif (!$ContactItem.FirstName -and !$ContactItem.LastName)
    {
        $ExchangeContact.NickName = $ContactItem.DisplayName;
        $ContactItem.FirstName = $ContactItem.DisplayName;
    }

    $ExchangeContact.DisplayName = $ExchangeContact.NickName;
    $ExchangeContact.FileAs = $ExchangeContact.NickName;

    # This uses the Contact Mapping above to save coding each and every field, one by one. Instead we look for a mapping and perform an action on
    # what maps across. As some methods need more "code" a fake multi-dimensional array (seperated by :'s) is used where needed.
    foreach ($Key in $ContactMapping.Keys)
    {
        # Only do something if the key exists
        if ($ContactItem.$Key)
        {
            # Will this call a more complicated mapping?
            if ($ContactMapping[$Key] -like "*:*")
            {
                # Make an array using the : to split items.
                $MappingArray = $ContactMapping[$Key].Split(":")
                # Do action
                switch ($MappingArray[0])
                {
                    "Email"
                    {
                        $ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::($MappingArray[1])] = $ContactItem.$Key.ToString();
                    }
                    "Phone"
                    {
                        $ExchangeContact.PhoneNumbers[[Microsoft.Exchange.WebServices.Data.PhoneNumberKey]::($MappingArray[1])] = $ContactItem.$Key;
                    }
                }                
            } else {
                $ExchangeContact.($ContactMapping[$Key]) = $ContactItem.$Key;            
            }

        }    
    }
    # Save the contact    
    $ExchangeContact.Save($ContactsFolder.Id);

    # Provide output that can be used on the pipeline
    $Output_Object = New-Object Object;
    $Output_Object | Add-Member NoteProperty FileAs $ExchangeContact.FileAs;
    $Output_Object | Add-Member NoteProperty GivenName $ExchangeContact.GivenName;
    $Output_Object | Add-Member NoteProperty Surname $ExchangeContact.Surname;
    $Output_Object | Add-Member NoteProperty EmailAddress1 $ExchangeContact.EmailAddresses[[Microsoft.Exchange.WebServices.Data.EmailAddressKey]::EmailAddress1]
    $Output_Object;
}

然后我制作了一个 foreach 循环文件并将其命名为 CopyGalToALLusers.ps1,它需要保存在与我刚刚制作的上一个文件相同的文件夹中。在这个 foreach 循环中,我正在查找我的整个域。您可以选择不同的 OU 或其他。只需查看此处解释的 Get-Mailbox https://technet.microsoft.com/en-us/library/Bb123685(v=EXCHG.150).aspx。您也可以更改保存联系人的文件夹名称。只需用不同的名称代替 OrgContacts。

   $mailboxes = Get-Mailbox -OrganizationalUnit "indy.int/Esco Users"
foreach ($mailbox in $mailboxes)
{
. "C:\Program Files\Microsoft\Scripts\Copy Global Contact to Users\Copy-OrgContactsToUserContacts.ps1" -Mailbox $mailbox.alias -FolderName OrgContacts
}

最后一步是手动运行文件是我使用 foreach 循环制作的 Exchange PowerShell 或在任务计划程序中安排它。

.\CopyGalToALLusers.ps1

为了安排它并使其用户成为正确的 powershell,我去了任务计划程序并将其设置为每天凌晨 2 点运行。在要运行的程序框中,我输入了以下内容。这是 server2012r2 上的 Exchange 2010。第一部分不仅使它打开 powershell,而且连接到交换,以便它理解你的命令。我找到合适的字符串放在这里的方法是我查看了我的交换 powershell 图标的属性并复制了整个内容。最后一行是你的 forloop 脚本的位置。只需添加一个分号和“.'forloop 文件的位置'”。您也可以在执行计划任务之前将其粘贴到命令行中进行测试。

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -command ". 'C:\Program Files\Microsoft\Exchange Server\V15\bin\RemoteExchange.ps1'; Connect-ExchangeServer -auto -ClientApplication:ManagementShell;  ". 'C:\Program Files\Microsoft\Scripts\Copy Global Contact to Users\CopyGALtoAllUsers.ps1'"
于 2015-08-26T15:51:22.127 回答