PowerShellから全社員に予定を投入したい

この投稿は PowerShell Advent Calendar 2016 に参加しています。

Facebookでリスクエストを貰ったので、PowerShellから全社員のメールボックスに創立記念日の予定を入れるPowerShellを作ってみたいと思います。

Exchangeでは、各種管理用のPowerShellコマンドレットが用意されておりますが、各ユーザーのメールボックスのアイテムを直接操作するコマンドは基本的には有りません。

ただし、EWS(Exchange Web Services)というAPIが用意されておりますので、そちらを利用する事によりメールボックスにアプローチできます。また、その際にアプリケーション偽装(ApplicationImpersonation)権限を利用する事により、「そのユーザーに成り代わって」そのアイテムを利用できます。

今回は、目的が明確ですので一番近いサンプルスクリプトとして以下のstack overflowに回答例として提示されているスクリプトを元に、作成用のコマンドを作ってみます。

How to import meetings into office365 (EWS and Powershell?)

変更している所は赤字で記載します。

function Create-Appointment
{
    [CmdletBinding()]
    param(
        [Parameter(Position=0, Mandatory=$true)] [string]$MailboxName,
        [Parameter(Position=1, Mandatory=$true)] [string]$Subject,
        [Parameter(Position=2, Mandatory=$true)] [DateTime]$Start,
        [Parameter(Position=3, Mandatory=$true)] [DateTime]$End,
        [Parameter(Position=4, Mandatory=$true)] [AllowEmptyString()] [string]$Location,
        [Parameter(Position=5, Mandatory=$true)] [AllowEmptyString()] [string]$Body,
        [Parameter(Position=6, Mandatory=$true)] [Boolean]$IsAllDayEvent,
        [Parameter(Position=7, Mandatory=$true)] [PSCredential]$Credentials
    )
    Begin
    {
        $EWSDLL = (($(Get-ItemProperty -ErrorAction SilentlyContinue -Path Registry::$(Get-ChildItem -ErrorAction SilentlyContinue -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Web Services'|Sort-Object Name -Descending| Select-Object -First 1 -ExpandProperty Name)).'Install Directory') + "Microsoft.Exchange.WebServices.dll")
        if (Test-Path $EWSDLL)
        {
            Import-Module $EWSDLL
        }
        else
        {
            "$(get-date -format yyyyMMddHHmmss):"
            "This script requires the EWS Managed API 1.2 or later."
            "Please download and install the current version of the EWS Managed API from"
            "http://go.microsoft.com/fwlink/?LinkId=255472"
            ""
            "Exiting Script."
            exit
        }

        $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
        $service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
        $creds = New-Object System.Net.NetworkCredential($Credentials.UserName.ToString(),$Credentials.GetNetworkCredential().password.ToString())
        $service.Credentials = $creds
        $Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
        $Compiler=$Provider.CreateCompiler()
        $Params=New-Object System.CodeDom.Compiler.CompilerParameters
        $Params.GenerateExecutable=$False
        $Params.GenerateInMemory=$True
        $Params.IncludeDebugInformation=$False
        $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=@'
  namespace Local.ToolkitExtensions.Net.CertificatePolicy{
    public class TrustAll : System.Net.ICertificatePolicy {
      public TrustAll() { 
      }
      public bool CheckValidationResult(System.Net.ServicePoint sp,
        System.Security.Cryptography.X509Certificates.X509Certificate cert, 
        System.Net.WebRequest req, int problem) {
        return true;
      }
    }
  }
'@ 
        $TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
        $TAAssembly=$TAResults.CompiledAssembly
        $TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
        [System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
        $service.AutodiscoverUrl($MailboxName,{$true})
        #"Using CAS Server : " + $Service.url
        $service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
        $folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Calendar,$MailboxName)
        $Calendar = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
        $Appointment = New-Object Microsoft.Exchange.WebServices.Data.Appointment -ArgumentList $service
        $Appointment.Start = $Start
        $Appointment.End = $End
        $Appointment.Subject = $Subject
        $Appointment.Location = $Location
        $Appointment.Body = $Body
        $Appointment.IsAllDayEvent = $IsAllDayEvent
        $Appointment.Save($Calendar.Id,[Microsoft.Exchange.WebServices.Data.SendInvitationsMode]::SendToNone)
    }
}
こちらを実行するためには、まず実行するアカウント(ここでは、仮にews_userとします)にApplicationImpersonation権限を付与します。ExchangeにPower Shellで接続し、以下のコマンドレットを実行します。
New-ManagementRoleAssignment –Name:EWSCalendarApplication -Role:ApplicationImpersonation –User:ews_user

続いて、PowerShellを実行するコンピュータにEWS Managed APIをインストールします。

PowerShellを開き、上記コードを実行し、Create-AppointmentのFunctionを登録します。

最後に、以下のコマンドを実行します。最初の行では実行アカウントのID/PASSを入力します。

$LiveCred = Get-Credential
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $LiveCred -Authentication Basic -AllowRedirection
Import-PSSession $Session -AllowClobber

$results = Get-Mailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited | select WindowsEmailAddress

foreach ($result in $results){
    Create-Appointment -MailboxName $result.WindowsEmailAddress -Subject "創立記念日" -Start "2017/07/01" -End "2017/07/01" -Body $null -Location $null -IsAllDayEvent $true -Credential $LiveCred
}

これで、来年の7/1に終日イベントとして[創立記念日]が全ユーザーMBXに作成されます。

 

他にも、これを応用すれば会議室の利用状況を集計したり、役員の今日の予定の印刷用の元データを抜いたりできます。

また、このコマンドを行うには対象となるメールボックスが先に作成されている必要があります。メールボックスはNew-Mailboxコマンドなどで作成した時点では実体は作成されていません。ユーザー自身のログイン以外にも、以下の様な動作を行う事により作成されますので、エラーが出て登録出来ないメールボックスには試してみると良いでしょう。

  1. メールの受信(例えば、メールの開通通知などの名目でテストメールを送る)
  2. 受信トレイルールの作成(管理者がNew-InboxRuleコマンドレットで作成できます。ダミーのルールを作成し、すぐに消せば影響は無いと思います)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です