読者です 読者をやめる 読者になる 読者になる

からめもぶろぐ。

SharePoint が得意なフレンズなんだね!すごーい!

SPClient 0.1 をリリースしました

PowerShell で CSOM を使って SharePoint を操作するためのモジュールを 0.1 版として公開しました。

より PowerShell らしくシンプルに書けるようなコマンドレットを提供します。
特に ClientContext.Load メソッドのラムダ式を JSOM ライクに文字列で指定できます!

PowerShell Gallery にも公開しています。

www.powershellgallery.com

頑張って英語で書いてみましたが、英語力がアレなので間違いなどあれば指摘いただきたいです…。

JSOM の executeQueryAsync で使われている Function.createDelegate について

JSOM のサンプルを見ると当たり前のように使われている Function.createDelegate について。

引数に this を渡しているのでだいたい想像が付くとは思いますが、JavaScript の this の面倒なアレを解決してくれるんですね。

<asp:Content ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <script type="text/javascript">
        var obj = {
            web: null,
            retrieveTitle: function () {
                var ctx = SP.ClientContext.get_current();
                this.web = ctx.get_web();
                ctx.load(this.web);
                ctx.executeQueryAsync(this.onSuccess); // NG
                ctx.executeQueryAsync(Function.createDelegate(this, this.onSuccess)); //OK
            },
            onSuccess: function () {
                alert(this.web.get_title());
            }
        };
        SP.SOD.executeFunc("sp.js", "SP.ClientContext", function () {
            obj.retrieveTitle();
        });
    </script>
</asp:Content>

ちなみに Function.createDelegate は MicrosoftAjax.js に定義されています。

データ取得の概要
Function.createDelegate 関数

PowerShell から SharePoint Online の REST API を叩いてみる

この記事は「PowerShell Advent Calendar 2016」の参加記事です。

qiita.com

前回の記事で「REST API を Invoke-RestMethod で叩けるかも」と丸投げなことを書いてしまったので、実際にやってみたいと思います。

blog.karamem0.jp

OAuth2 認証についておさらい

SharePoint Online の REST API を実行するには、Azure Active Directory (AAD) の OAuth2 認証が必要です。AAD ではアクセス トークンを取得するためにいくつかの方法 (grant_type) をサポートしています。

  • authorization_code (https://login.windows.net/common/oauth2/authorize)
  • client_credentials (X.509 証明書)
  • password (ユーザー名とパスワード、非推奨)
  • device_code (デバイス コード)

authorization_code については Web ブラウザーを使用するアプリケーション (Web アプリケーションやモバイル アプリケーション) が前提なので、PowerShell の場合はそれ以外の方法を選択することになります。password が最も簡単ですが非推奨なので除外して、実質的には device_code か client_credentials の二択になります。それぞれを簡単に比較すると以下の通りになります。

grant_type 手間 無人化
device_code 少ない 不可
client_credentials 多い

どちらもメリットとデメリットがありますので目的に応じて使い分けるのがいいと思います。今回は device_code による認証を行います。

試してみる

手順については松崎さんのブログを参照ください。

blogs.msdn.microsoft.com

アプリケーションの登録

現時点ではプレビュー段階ですが、新しい Azure 管理ポータルからも AAD のアプリ登録ができるようになりましたので、適当な名前で登録します。[リダイレクト URI] は使わないので http://localhost/ としています。

f:id:karamem0:20161215235141p:plain

使用する API に [Office 365 SharePoint Online] を選択します。

f:id:karamem0:20161215235152p:plain

アクセス許可を選択します。今回はサンプルなのでフル コントロールを付けていますが、適切なアクセス許可を選択してください。

f:id:karamem0:20161215235156p:plain

これで事前準備は完了です。

スクリプトの作成

github.com

C# だと WebClient や HttpClient を使って面倒な処理を書くことになりますが、PowerShell の場合は Invoke-RestMethod を使ってかなりすっきり書くことができます。

$tenantId = "<tenantid>"
$resourceUri = "<resourceuri>"
$clientId = "<clientid>"

# デバイス コードの取得
$uri = "https://login.microsoftonline.com/" + $TenantId + "/oauth2/devicecode?" + `
       "resource=" + [System.Uri]::EscapeDataString($resourceUri) + "&" + `
       "client_id=" + $clientId
$headers = @{
    "Accept" = "application/json"
}
$result = Invoke-RestMethod -Method "Get" -Uri $uri -Headers $headers

$userCode = $result.user_code
$deviceCode = $result.device_code

Write-Output $userCode
Start-Process "https://aka.ms/devicelogin"

Read-Host | Out-Null

# トークンの取得
$uri = "https://login.microsoftonline.com/" + $TenantId + "/oauth2/token"
$headers = @{ 
    "Accept" = "application/json"
    "Content-Type" = "application/x-www-form-urlencoded"
}
$body = "resource=" + [System.Uri]::EscapeDataString($resourceUri) + "&" + `
        "client_id=" + $clientId + "&" + `
        "grant_type=device_code&" + `
        "code=" + [System.Uri]::EscapeDataString($deviceCode)

$result = Invoke-RestMethod -Method "Post" -Uri $uri -Headers $headers -Body $body

$accessToken = $result.access_token

# サイトのタイトルを取得
$uri = $resourceUri + "/_api/web/title"
$headers = @{ 
    "Accept" = "application/json"
    "Authorization" = "Bearer " + $accessToken
}
$result = Invoke-RestMethod -Method "Get" -Uri $uri -Headers $headers
Write-Output $result.value

# ドキュメントの一覧を取得
$uri = $resourceUri + "/_api/web/getfolderbyserverrelativeurl('/Shared%20Documents')/files"
$headers = @{ 
    "Accept" = "application/json"
    "Authorization" = "Bearer " + $accessToken
}
$result = Invoke-RestMethod -Method "Get" -Uri $uri -Headers $headers
$result.value | select Name, TimeCreated, TimeLastModified

実行

実行するとプロンプトにコードが表示されます。認証が行われるまでスクリプトは入力待ち状態になっています。

f:id:karamem0:20161215235254p:plain

合わせてブラウザーが起動するので、コンソールに表示されたコードを入力します。

f:id:karamem0:20161215235302p:plain

アプリケーション名を確認して [続行] をクリックします。組織アカウントでのサインインを要求されますのでユーザー名とパスワードを入力します。

f:id:karamem0:20161215235311p:plain

サインインすると完了になります。ブラウザーは閉じても構いません。

f:id:karamem0:20161215235322p:plain

プロンプトで Enter キーをクリックするとスクリプトを再開します。SharePoint Online からサイトのタイトルとドキュメント ライブラリのファイルの一覧を取得できていることがわかります。

f:id:karamem0:20161215235329p:plain

まとめ

SharePoint Online 単体だと CSOM のほうが便利かもしれませんが、他の Office 365 サービスと連携する場合などは、REST API を使うと統一された方法で書くことができそうです。

おまけ

ちなみに、CSOM がどうやって SharePoint Online に認証をしているかというと、BPOSIDCRL という方式を使っているようです。BPOS の名前の通り、レガシーな方法なので、いつまでサポートされるんでしょうか。気になります。

RFC 6749 - The OAuth 2.0 Authorization Framework

SharePoint を PowerShell から操作する方法まとめ

この記事は「PowerShell Advent Calendar 2016」の参加記事です。

qiita.com

今は SharePoint をメインでやっているこのブログもいつの間にか PowerShell タグがついた記事が結構増えているわけですが、SharePoint の構築や運用にあたって PowerShell は欠かせない存在になりました。過去のバージョンの SharePoint では stsadm というコマンド ライン ツールを使っていたそうですが、今はお役御免となりました。*1
また SharePoint Online の登場により、新しく追加されたライブラリもありますので、合わせて整理したいと思います。

SharePoint Server (オンプレミス) の場合

SharePoint をインストールすると、[スタート] メニューに [SharePoint 20nn 管理シェル] が登録されます。ここから SharePoint を操作するためのさまざまなコマンドレットを実行できます。スクリプトで実行する場合や、ISE を使いたい場合は、以下のコマンドレットを実行することで、SharePoint の管理シェルを実行できるようになります。

Add-PSSnapin "Microsoft.SharePoint.PowerShell"

SharePoint 管理シェルの実体は Microsoft.SharePoint.dll (およびその関連ライブラリ) なので、SharePoint のサーバー オブジェクト モデルがそのまま適用されます。そのため、SharePoint 開発経験のある開発者には非常に取っつきやすいです。また、便利なコマンドレット群も提供されています。*2

Visual Studio で SharePoint ソリューション開発をするときに、デプロイ前後のコマンドで PowerShell スクリプトを呼び出してデバッグ先のサイトにテスト データを投入すると非常に捗ります。ただし、そのとき注意しないといけないのは、Visual Studio は x86 プロセスなので PowerShell も x86 が呼び出されてしまう点です。SharePoint の管理シェルは x64 でしか動作しないので、PowerShell も x64 で動かすよう工夫する必要があります。

"%WINDIR%\SysNative\WindowsPowerShell\v1.0\powershell.exe" -ExecutionPolicy Unrestricted -File "$(ProjectDir)\Hoge.ps1"

SharePoint Online の場合

SharePoint Online では、サーバーにログインすることはできないので、代わりに SharePoint Online 管理シェルが提供されています。しかし、ここで特に注意しなければならないのは、SharePoint Online 管理シェルでできることは非常に限定的であるという点です。実質的にはユーザーの管理とサイト コレクションの管理しかできません。ならばどうするかというと、SharePoint クライアント ライブラリ コード、すなわち CSOM を使うことになります。というより、CSOM でできないことを SharePoint Online 管理シェルがカバーしているという感じのようです。

CSOM は PowerShell を考慮して作られているわけではないので、割と辛い目に遭います。具体的にいうと、CSOM では Load メソッドで関連プロパティを読み込むときに Include という Entity Framework みたいな呼び出しをするわけですが、ラムダ式なので PowerShell では死にます。やればできるみたいですが、そこまでやるメリットもなさそう。

www.itunity.com

試したことないですが、REST API を Invoke-RestMethod で呼び出す方法もあるかもしれません。でも今度は認証の壁が立ちはだかるのです…。こちらもやればできますけど。

blog.karamem0.jp

*1:たぶん 2007 以前だと思いますが、その頃の SharePoint は触ったことがないので詳しいことはわかりません。

*2:個人的には Get-SPWeb が最強で、C# で SPSite.OpenWeb() するたびに「これ PowerShell だと一発なのに…」と思っています。