powershellを使ってsharepoint上のファイルを制御する

やりたいこと
---
sharepoint online上にあるドキュメントライブラリに格納されたファイルに対して、
サーバからプログラムでファイル操作する、という実装を作ったんですが、
人によって書いてあることがまちまちで結構嵌ったので出来上がった結果をメモしておきます。

処理内容は以下の通りです。
・sharepoint上に特定のファイルがあるかどうかを判定する
・sharepoint上の特定のファイルをサーバにダウンロードする
・サーバ上の特定のファイルをsharepoint上に名前を変えてアップロードする
・sharepoint上の特定のファイルを削除する


事前準備
---
sharepoint用のAPIが必要になるので、MSのサイトからCSOMをダウンロードし、サーバにインストールします。
参考：https://blogs.technet.microsoft.com/sharepoint_support/2017/12/20/tips-for-csom-on-powershell/
今回の作業ではNuget版のMicrosoft.SharepointOnline.CSOMをインストールしています。



作業環境
---
sharepointのサイトurl:　http://hogehoge.com/sites/FUGA
ドキュメントライブラリ名：　DocLib
ドキュメントライブラリ内のフォルダ：　Folder1
サーバ側のフォルダ：　D:\FUGA\Folder1
対象ファイル：　File1.txt


共通手順：CSOMのロードとsharepointへの認証
---
インストールしたCSOMのロードとSharepointへの認証はどの処理でも共通の手順になります。
処理完了後は`$Context.Dispose()`で〆るようにしましょう

```powershell:common
# SharepointOnline用のCSOMをロードする
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client") > $null
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime") > $null

$UserName = "<SharepointUser>"
$Password = ConvertTo-SecureString "<SharepointPassword>" -AsPlainText -Force
$SiteURL = "http://hogehoge.com/sites/FUGA"

# 認証
$credential = New-Object Microsoft.SharePoint.Client.SharePointOnlineCredentials($UserName, $Password)
$Context = New-Object Microsoft.SharePoint.Client.ClientContext($SiteURL)
$Context.Credentials = $credential

$objWeb = $Context.Web
$Context.Load($objWeb)
$Context.ExecuteQuery()
```


SharepointPasswordは実際のパスワードではなく暗号化された文字列（SecureStirng）です。
ConvertTo-SecureStringを使ってプログラム内で復号しています。
以下の記事に手順は詳しいのでそちらを参照して頂ければ。
参考：https://qiita.com/Takeru/items/c8c769f88e8ccb06ffe3


上記処理を流した後に用途に応じて以下の処理を流します。

sharepoint上に特定のファイルがあるかどうかを判定する
---
sharepointのドキュメントライブラリDocLib上にFolder1/File1.txtが存在しているかを確認します。

```powershell:check
$SPOLib = "DocLib"
$SPOPath = "Folder1"
$FileName = "File1.txt"

# sharepoint上のファイルは以下のような形で表す事ができる
$SPOFile = $objWeb.ServerRelativeUrl + "/" + $SPOLib + "/" + $SPOPath + "/" + $FileName

Try {
  $Check = $Context.Web.GetFileByServerRelativeUrl($SPOFile)
  $Context.Load($Check)
  $Context.ExecuteQuery()
}
Catch {
  Write-Output "File NOT exist on Sharepoint.: $FileName"
  $Context.Dispose()
  exit 0
}
Write-Output "File exists on Sharepoint.: $FileName" 
$Context.Dispose()
exit 0
```


sharepoint上の特定のファイルをサーバにダウンロードする
---
sharepoint上のFolder1/File1.txtをサーバのE:\FUGA\Folder1にダウンロードします。
`[System.IO.FileMode]::Create`は既存のファイルを消して再作成するモードなので
必要に応じて適切なモードなり事前チェックなりが必要です

```powershell:download

# sharepointとローカルそれぞれのファイル格納先を指定
$SPOLib = "DocLib"
$SPOPath = "Folder1"
$Path = "E:\FUGA\Folder1"
$FileName = "File1.txt"

$SPOFile = $objWeb.ServerRelativeUrl + "/" + $SPOLib + "/" + $SPOPath + "/" + $FileName
$TargetFile = Join-Path $Path $FileName

# sharepoint上のファイルを開いてからローカルファイルの書き込みストリームを開き、データコピーを行う
$OpenFile = [Microsoft.SharePoint.Client.File]::OpenBinaryDirect($Context, $SPOFile)
$WriteStream = [System.IO.File]::Open($TargetFile,[System.IO.FileMode]::Create)
$OpenFile.Stream.CopyTo($WriteStream)

$WriteStream.Close()
$Context.Dispose()

exit 0
```


サーバ上の特定のファイルをsharepoint上に名前を変えてアップロードする
---
サーバ上のFile1.txtをDocLib上の**Folder1/backup**に**File1_Backup.txt**としてアップロードします。
対象フォルダやファイル名を変えて処理したい場合は以下のような形にすれば可能です。

```powershell:upload
# ドキュメントライブラリ上のアップロード先フォルダをロードする
$SPOLib = "DocLib"
$SPOPath = "Folder1"

$UploadFolder = $SPOLib + "/" + $SPOPath + "/backup"
$Ufolder = $objWeb.GetFolderByServerRelativeUrl($objWeb.ServerRelativeUrl + $UploadFolder)
$Context.Load($Ufolder)
$Context.ExecuteQuery()

# ローカルサーバ側のアップロード元ファイルを定義
$Path = "E:\FUGA\Folder1"
$FileName = "File1.txt"
$TargetFile = Join-Path $Path $FileName
$LocalFile = Get-ChildItem $TargetFile

# sharepoint側のファイルストリームを開いてファイルをアップロード（Overwriteは必要に応じて変更）
$FileStream = New-Object System.IO.FileStream($LocalFile,[System.IO.FileMode]::Open)
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.ContentStream = $FileStream
$BackupName = (Get-ChildItem $TargetFile).BaseName + "_" + (Get-ChildItem $TargetFile).Extension
$FileCreationInfo.Url = $BackupName

$UpLoad = $UFolder.Files.Add($FileCreationInfo)

$Context.Load($UpLoad)
$Context.ExecuteQuery()

$FileStream.Close()

$Context.Dispose()
exit 0
```


sharepoint上の特定のファイルを削除する
---
DocLib上のFolder1/File1.txtを削除します。ファイルのみ削除し、フォルダは削除しません。

```powershell:delete.ps1
$SPOLib = "DocLib"
$SPOPath = "Folder1"
$FileName = "File1.txt"

$DelFilePath = $SPOLib + "/" + $SPOPath
$DelFile = $objWeb.GetFolderByServerRelativeUrl($objWeb.ServerRelativeUrl + $DelFilePath + "/" + $FileName)

# Delete File
$DelFile.DeleteObject()
$Context.ExecuteQuery()
exit 0
```


ごちゃっとした記事ですが初投稿ということで多めに見ていただければ。