Exchange 2013试图复制全局地址簿GAL到所有用户,所以它显示在Android和IOS离线



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

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

我的首席运营官希望我们的全球地址簿在每个人的设备(即Android, IOS, Windows等)离线时显示。我已经找到了一种方法,在Exchange Powershell使用史蒂夫·古德曼的方法/代码-> http://www.stevieg.org/2012/02/importing-global-address-list-entries-into-a-users-contacts-folder一次一个用户。在对我自己的用户名这样做之后,我能够在我的手机离线上看到GAL信息。而且,如果有人从他们的手机打电话给我,它现在会显示在我的手机上,并通过从导入的GAL中提取他们的名字。

基本上,使用他的代码,我将全局地址簿作为单独的地址簿复制给每个人。如果你在Outlook中查看,你会看到你的联系人,你会看到名为OrgContacts的新地址簿(在这种情况下)。当你的设备下次同步到exchange时,这个地址簿也会同步整个公司都和你在一起。我们有几百个用户,所以这不是什么大问题。 到目前为止,我使用的代码是一次一个用户。我需要帮助它找到所有的用户名和执行。我尝试在运行字符串通配符,但没有工作。如果有的话,我也愿意用一种完全不同的方式来完成这个任务。

非常感谢您的宝贵时间,

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

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

这是Exchange power shell代码…

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 FilesMicrosoftExchangeWeb Services1.2Microsoft.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;
}
到目前为止,我使用的代码是一次一个用户。我需要帮助它找到所有的用户名和执行。我尝试在运行字符串通配符,但没有工作。如果有的话,我也愿意用一种完全不同的方式来完成这个任务。

您只是想在Exchange Org中输入邮箱的名称吗?如果是这种情况,只需使用Get-Mailbox eg

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

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

如果你想在计划任务中使用Exchange Management shell cmdlet,那么你需要做类似http://mikepfeiffer.net/2010/02/creating-scheduled-tasks-for-exchange-2010-powershell-scripts/

干杯格伦

首先,Glen,谢谢你给我的链接,让我思考这个问题。你可能会把它都说出来,但我在这个过程中学到了一些东西。

使用风险自负!我不是程序员。我不会只在生产机器上这样做,但这取决于你。这将把所有联系人复制到所有用户。下次他们的手机或设备同步时,离线时所有联系人都可以使用。它也可以在Outlook离线的联系人中使用。我们只有150人。我想我不会再建议大家这么做了。这段视频只播放给我们用了一个多小时。可能有一种方法可以构建GAL,然后将其复制到每个用户,而不是像这样一遍又一遍地构建GAL的副本。但是,我对这个的了解还不够。

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

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

首先,我保存这段代码(第一个代码块)为Copy-OrgContactsToUserContacts。ps1 from Steve Goodmans method/code -> http://www.stevieg.org/2012/02/importing-global-address-list-entries-into-a-users-contacts-folder

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 FilesMicrosoftExchangeWeb Services1.2Microsoft.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。您也可以更改保存联系人的文件夹名称。只需在"组织联系人"中添加一个不同的名称。

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

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

.CopyGalToALLusers.ps1

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

C:WindowsSystem32WindowsPowerShellv1.0powershell.exe -command ". 'C:Program FilesMicrosoftExchange ServerV15binRemoteExchange.ps1'; Connect-ExchangeServer -auto -ClientApplication:ManagementShell;  ". 'C:Program FilesMicrosoftScriptsCopy Global Contact to UsersCopyGALtoAllUsers.ps1'"

最新更新