So-net無料ブログ作成
検索選択
PowerShell ブログトップ
前の10件 | -

return [PowerShell]

他のプログラミング言語と同様に PowerShell にも Return ステートメントは存在する。PowerShell では明示的に Return ステートメントを使用しなくても(複数の)値を返すことができるために、無くても困らないかもしれない。

ただ、PowerShell の Return ステートメントはかなり癖がある。PowerShell を使い始めた頃から気になってしようがなかったのだけど、誰も指摘しない、、、あまり気にしないのかなあ。なんか、こんなシチュエーション多いなぁ。

 下記のスクリプトを動作させたときの結果を正確に予想できますか?

0..9 | %{ 
    foreach ($i in 10..19) { 
        return $i; 
    } 

--- 
0..9 | %{ 
    10..19 | %{ 
        return $_; 
    } 

--- 
foreach ($i in 0..9) { 
    10..19 | %{ 
        return $_; 
    } 

--- 
foreach ($i in 0..9) { 
    foreach ($j in 10..19) { 
        return $j; 
    } 

私は未だに迷うことがある。


日時 [PowerShell]

業務系の作業に関わるようになると、日時の情報を扱うことが増えるような気がする。以前は「西暦が 4 で割り切れたら閏年だけど、100 で割り切れたら平年、でも、 400 で割り切れたら...」なんて自分でやっていたけど、今ではライブラリが全て受け持ってくれる。

社会人になって時計が読めないなんて人は殆どいないだろうけど、希望する日時への変換となると結構勘違いしてしまうことがある。割とよく出てくる変換を紹介します。文法は PowerShell だけれど、.NET Framework であれば翻訳も難しくないと思う。System.DateTime 型の $cur を変換してみます。

  1. 当日の一番早い時刻
    $cur.Date
    まずは小手調べ。時刻の情報をクリアしてしまえば当日の午前 0 時 0 分 0 秒になる。
  2. 当日の一番遅い時刻
    $cur.AddDays(1).Date.AddTicks(-1)
    漢字 1 字違うだけだけど、意外に面倒。翌日の一番早い時刻を求めて、Tick(DateTime の最小単位)を引く。
  3. 今週の日曜日
    $cur.AddDays(-$cur.DayOfWeek).Date
    日曜日が 0、月曜日が 1 、、、となる DayOfWeek プロパティの特性を利用。状況によって Date プロパティは無くてもいい。
  4. 今月の 1 日
    $cur.AddDays(-$cur.day+1).Date
    前項とよく似た変換。
  5. 今月の末日
    $cur.AddDays(-$cur.day+1).Date.AddMonths(1).AddTicks(-1)
    ちょっと長いけど、結構素直でしょ。途中に置いた Date プロパティがカギ。

多少長いものもあるけど、みんな 1 行で変換できてしまう。面倒な計算はライブラリ内で実行されてるとは思うけど、PC がフリーズするほど負荷がかかるとは思えないので効率は気にしないことにする。

 


区間 [PowerShell]

PowerShell に限らずプログラムの中で時刻を扱うことはよくある。フレームワークやライブラリがある程度サポートしてくれているので理屈さえ解っていれば問題ないのだけど、慣れないうちはハマってしまうことも致し方ないのかもしれない。

仕事中に聞こえてきた話をネタにしてみました。

聞こえてきた範囲で要約すると「昨日一日分のイベントログを採取して」ということらしい。

以前は、イベントログを採取するのに利用されていたのは WMI が一般的だったらしい。私は WMI はあまり詳しくないので聞いた話になってしまうけど、「SQL のような構文が使えて便利だけどちょっと遅い」というものだということ。

イベントログくらいなら PowerShell でもスクリプトなしで使える。

Get-EventLog System -Newest 10

 とやれば、System イベントの最新の 10 件を取得するという意味になる。Get-EventLog CmdLet は -After と -Before というパラメーターを使用できるので、例題のように範囲を絞ることも簡単にできる。

時刻の範囲を指定する方法はいくつかある。「8 時台」のデータだけを取得したかったら、時刻のプロパティが 8:00:00 以降で 9:00:00 より前という判断が紛れがなくていい。

8:00:00 以降で 8:59:59 以前のほうが素直に見えるけど、実はちょっとまずいことがある。時刻の情報を秒単位で持つような(ex: Unix Epoch)ものであればいいのだけど、.NET Framework の System.DateTime 構造体は秒以下の情報まで保持することができる。確率的には相当低いと考えられるけど、まずいと思うものを放置しておくのも気が咎める。

Get-EventLog CmdLet の -After と -Before は、文字通り「~より後」と「~より前」をあらわす。なので、ここで指定した時刻が含まれないという落とし穴がある。なので、ちょっと小細工が必要。

$today= (Get-Date).Date
$yesterday= $today.Add((New-Object System.TimeSpan(-1,0,0,-1)))
Get-EventLog System -After $yesterday -Before $today

 イベントログを取得する Windows の API を確認したところ、EVENTLOGRECORD という構造体を使用しており、イベント発生日時(TimeGenerated) が Unix Epoch のようなので(違ってたらごめんなさい)秒以下は常に 0 になっていると思われます。

同じ動作をするように Where-Object を使用して書き換えるとこんな感じになる。

$today= (Get-Date).Date
$yesterday= $today.AddDays(-1)
Get-EventLog System | ?{ $_,TimeGenerated -ge $yesterday -and $_.TimeGenerated -lt $today }

こちらの方が若干スマートに見えるのだけど、チョット遅い。


アカウント [PowerShell]

SharePoint Online の試用を初めて 2 週間ほどが経過。当初の予想通り、、、というわけでもないのだけれど、結構壊しまくってる気がする。権限のあたりを適当に(= いい加減に)弄っているうちに、管理者でサインインができなくなってしまい、新たにアインアップという手順を踏むこと数回。回避(あるいは回復)手段を理解しているならいいのだけど、さすがにそんなスキルはないのは自分がよく知っているので、深く考えずにリスタートしている!!

新たにサインアップすることで、真っ新な環境が使える様になるのはいいのだけど、検証(これをメインに使ってる)環境が消えてしまうのは結構痛い。検証用のユーザーアカウントやセキュリティグループを登録し直して、ユーザーをグループに加えるという、難しいものではないけど結構イライラする。

今回のスクリプトは常時使うようなものではないかもしれない。実運用に使えない訳ではないけど、検証用環境を構築するためにしか利用実績がない。

function global:New-SPOUsers([string] $FileName) {
    $sku= (Get-MsolAccountSku).AccountSkuId;
    Import-Csv (Convert-Path $FileName) | %{
        $mail= "$($_.Alias)@$($_.Tenant).onmicrosoft.com";
        $site= $cc.Url
        $u= New-MsolUser `
            -UserPrincipalName $mail `
            -Department $_.Department `
            -DisplayName $_.Alias `
            -ForceChangePassword:$False `
            -LicenseAssignment $sku `
            -Password $_.Password `
            -PasswordNeverExpires:$True `
            -UsageLocation $_.UsageLocation;
        if ($_.Administrator -eq $true) {
            Add-MsolRoleMember -RoleName "Company Administrator" `
                -RoleMemberEmailAddress $mail | Out-Null;
        }
        $group= $_.Group
        if ($group -ne "") {
            $g= Get-MsolGroup -GroupType Security | ?{ $_.DisplayName -eq $group }
            if ($g -eq $null) { $g= New-MsolGroup -DisplayName $group }
            Add-MsolGroupMember -GroupMemberObjectId $u.ObjectId -GroupObjectId $g.ObjectId
        }
    }
}

ここでは Office 365 と SharePoint Online の機能しか使用していないけれど、Exchange Online を併用するなら、配布グループ等も含めることも難しくはない。検証環境に特化するなら動的配布グループを使用するのも面白いかもしれない。


SharePoint Online と CSV 3 [PowerShell]

SharePoint Online が管理する情報をスクリプト(PowerShell) で弄るなら CSOM (Client Side Object Model) がほぼ必須。ネームスペースを見ると Microsoft.SharePoint.Client となっている事からも想像できるように SharePoint のクライアントサイドの機能を収めたものとなっている。なお、今日のスクリプトはここで紹介している初期化処理が前提になっているのでご注意ください。

なお、サンプルとして使用したデータは 駅データ,jp で公開している事業者データと路線データを使用してみた。やりたいことは「事業者データを取り込んであるサイトに、路線データを追加で取り込む。その際に事業者コードの結合を行う」ということ。なお、このサイトの CSV データは BOM なし UTF-8 形式が使われている。以下の操作を実行する前に、BOM 付き UTF-8 に変換している。前にも書いたように、BOM 付き UTF-8 は一般的ではないのだけど、Windows 環境では de facto standard。

以下のスクリプトは $Name にはリストの名称が、$File には CSV のフルパスが、そして、$Target に結合するリストの名称が設定されている前提で動作する。

  1. リストを構築する
    まず最初に、リストの側だけを構築する。この段階では列の型や列数といったものは一切不要。[リスト] であることと、リストの名称だけが解ればいい。
    $info= New-Object `
    Microsoft.SharePoint.Client.ListCreationInformation
    $info.Title=$Name
    $info.TemplateType= `
    [int][Microsoft.SharePoint.Client.ListTemplateType]::GenericList
    $list= $cc.web.Lists.Add($info)
    $cc.Load($list)
    $cc.ExecuteQuery()

    動作そのものは上記の通りなんだけど、最後の 2 行が CSOM の変った作法言うべきところ。CSOM ではオブジェクトの持つプロパティにアクセスする際に、プロパティをロードしてやる必要がある。これを怠ると当該プロパティは null のままなので、画面が真っ赤になるほどエラーを吐くことになってしまう。

  2. 型を定義する 前篇
    1. で作成したリストに対して列の型を追加していく。型定義は XML 片に含まれていて、要は名称と型、そして必要に応じて参照先のリストの Id と、結合する列名を含む。
    $u= $cc.Web.Lists.GetByTitle($Target)
    $cc.Load($u)
    $cc.ExecuteQuery()

    $o= $cc.Web.Lists.getByTitle($Name)
    "<Field DisplayName='line_name' Type='Text'/>",
    "<Field DisplayName='line_cd' Type='Number' />",
    "<Field DisplayName='line_name_h' Type='Text' />",
    "<Field DisplayName='line_name_k' Type='Text' />",
    "<Field DisplayName='company_cd' Type='Lookup' List='{$($u.Id)}' ShowField='company_cd' />" | %{
    $field= $o.Fields.AddFieldAsXml($_, $true, `
    ([Microsoft.SharePoint.Client.AddFieldOptions]::DefaultValue))
    $cc.Load($field)
    #$cc.ExecuteQuery()
    }
  3. 型を定義する 後編
    結合したリストを参照する場合、ちょっとした細工が必要。結合を構成している列(company_cd)の Id を取得する必要があるために、どうしても後付けにする必要がある。
    $x= $cc.Web.Lists.getByTitle($Name).Fields.getByTitle("company_cd")
    $cc.Load($x)
    $cc.ExecuteQuery()
    $tag= "<Field DisplayName='company_name' Type='Lookup' List='{$($u.Id)}' ShowField='company_name_h' FieldRef='$($x.Id)' ReadOnly='true' />"
    $field= $o.Fields.AddFieldAsXml($tag, $true, ([Microsoft.SharePoint.Client.AddFieldOptions]::DefaultValue))
    $cc.Load($field)
    $cc.ExecuteQuery()
  4. CSV ファイルを流し込む
    PowerShell は比較的簡単に CSV ファイルが扱える。読み取った CSV をオブジェクトに変えてくれるので結構使い勝手がいい。
    $csv=Import-Csv $File
    $cnt=$csv.Count
    $i=1
    $csv | %{
    Write-Progress "Copying" -PercentComplete (100*($i/$cnt))
    $info= New-Object Microsoft.SharePoint.Client.ListItemCreationInformation
    $item= $o.AddItem($info)
    $item.set_Item('line_name', $_.line_name)
    $item.set_Item('line_cd', $_.line_cd)
    $item.set_Item('company_cd', $_.company_cd)
    $item.set_item('line_name_k', $_.line_name_k)
    $item.set_item('line_name_h', $_.line_name_h)
    $item.Update()
    $cc.Load($item)
    $cc.ExecuteQuery()
    $i++
    }

という感じで処理してやれば、CSV データのインポートと共にリスト間の結合ができるようになる。ただ、既存のデータを扱えるようにするには、もう少し考えないといけない。


SharePoint Online と CSV 2 [PowerShell]

SharePoint Online にリストを構築する手順、これがびっくりするほど煩雑。よくもこんな UI を市場に出せたなぁと言われそうな代物だったりする。ベースとなっているのが SharePoint Server 2013 らしいけど、きっと本家も変り映えしないんだろうなぁ。

実際にはこんな手順になる。Office 365 のポータルにサインインし、SharePoint Online のサイトに移動した後の手順。

  1. 右上のギアのようなアイコンクリックしてメニューを開き、[アプリの追加] を選択する。
  2. [追加できるアプリ] の一覧から [カスタムリスト] 探し、アイコンをクリックする。
  3. ['カスタムリスト'の追加中] ポップアップで [名前] を入力して [作成] をクリックする。
  4. サイトコンテンツの一覧が表示されるので、3. で作成したカスタムリストを探して、アイコンをクリックする。
  5. リボンの [リスト] を開き、[リストの選択] をクリックする。
  6. ページ上部に [全般設定]、[権限と管理]、[通信] という設定項目が並ぶので、必要に応じて設定値を書き換える。
  7. [列] グループにある [列の作成] をクリックする。
  8. 列のプロパティを設定し [OK] をクリックする。
  9. 7.~8. を必要に応じて繰り返す。
  10. メニューを開き [サイトコンテンツ] を選択する。
  11. 4. と同様に対象のカスタムを探してアイコンをクリックする。
  12. リボンの [リスト] を開き、[ビューの変更] をクリックする。
  13. リストの表示項目、並び順、選択条件等を決め [OK] をクリックする。

と、ここまでやってやっとリストとビューの定義が出来上がる。実際にデータを入力するのであれば、このあとにその作業が続くことになるのだけれど、SharePoint Online に慣れていないと、何をすればいいのか確実に解らなくなるだろう。実際に操作してみたら解るけど、あっち飛び、こっち飛びと、マウス操作が非常に多いのでとても目が疲れる。

極めれば慣れたら作業時間は多少縮むだろうけどねぇ。まあ、極めたいとも思わないんだけど、、、。

こんな操作を繰り返すくらいなら、スクリプト作るのに苦労した方がまだ楽しい、と当初は思ったんだけど、SharePoint Online はスクリプト書くのも結構ストレスだったりする。今日一日(3/13 日曜日) 悩んで CSV のインポート機能(Join ができるように改良した)ものが出来上がった。

完全自動化とまではいかないけれど、ある程度使えると思うので、そのうち公開します。


SharePoint Online と CSV 1 [PowerShell]

SharePoint を利用して情報共有するものは、基本的なところではスプレッドシートのようなリストと、ファイルを表すライブラリ。それ以外に Web パーツを利用することもできるけど、その情報源にリストやライブラリを利用するということになる。

取り敢えずはリストのほうから弄ってみることにする。

とは言っても、当然のことだけどサインアップしたての SharePoint Online にはリストに含む情報が入っていない。手入力するのも邪魔臭いので、適当に CSV ファイルでも拾ってこようと web を彷徨っていたら、結構無料で使用できるファイルが存在している。郵便番号データとか駅データとか。

CSV ファイルのインポートはメニューから [アプリの追加] を選択し、[スプレッドシートのインポート] を実行してやればいい。データのインポートに Excel の機能を使用しているらしく、Excel で読めないものは使えないという事になってしまう。また、インターネット経由だからなのか、処理が異様に遅い。郵便番号データをインポートしたら 2 時間ほどかかった。

SharePoint Online では扱えるデータ量に制限がある。5,000 件以上の情報は利用できないし、回避手段もないらしい。ただ、この 5,000 件というのが、何を指しているのかはっきりしない。上記のデータには 10,000 件以上の項目を含むファイルもあるのだけれど、これらのファイルをインポートすること自体は何の問題もなかった。例えば、「函館本線の駅」みたいな条件(データをインポートしただけの状態では、このような直接的な指定はできません)で検索しようとすると拒否されてしまう。さらに、そのまま検索条件を放置して置いたら翌日には使える様になるとか、イマイチわけの解らん挙動をしめす。ちなみに、「函館本線の駅」でヒットするのは 99 件だったので、制限に引っかかるとも思えない。

CSV をインポートして作成したリストはちょっとした制限がある。SharePoint には 簡単な Join の機能があるのだけれど、それを利用するには外部キーに相当する列の [型] を [参照] 型にする必要がある。ところが、その方法が認められていない模様。まあ、自動でできないなら手作りする方向でチャレンジしてみることにする。

 


SharePoint Online - 3 [PowerShell]

前回のスクリプトに違和感を感じた方もいるかもしれません。本篇については、ほぼリアルタイムな検証結果となっているため、執筆時点で動作確認のとれたものだけを投稿しています。「SharePoint Online を PowerShell でイジる」というゴールから反れるのは納得しかねるので、一部情報を隠した格好のスクリプトとなってしまったかもしれません。実は、前回紹介したスクリプトは、(私にとっては)完全なものではありません。バグがあるというようなものではないのですが、当初の目的を果たすための細工が完璧に抜け落ちてしまっているという点が問題です。

前にもお話しした通り、SharePoint Online で提供される CmdLet は、サイトの管理作業を実施するためのものであって、サイトで提供する情報を管理するものではないということです。.NET Framework の柔軟性は、アセンブリの所在さえ解ればカスタマイズするのはスキル相当というところにあると考えていますので、これさえ克服できればほぼ万能なんじゃないかと過大評価してしまいそうです。

SharePoint のアセンブリでは、Microsoft.SharePoint.Client というネームスペースの元で、クライアントサイドに提供する機能を公開しています。これを PowerShell で弄ってみようというのが、本編の主題ということになります。Microsoft.SharePoint.Client が提供する機能全般を CSOM(Client Side Object Model) という名称で定義しているようですが、この機能を利用するためには更なる認証作業が必要となります。

[System.Reflection.Assembly]::LoadWithPartialName( `
    "Microsoft.SharePoint.Client");
[System.Reflection.Assembly]::LoadWithPartialName( ` "Microsoft.SharePoint.Client.Runtime");
function global:SharePointOnline($Site= "https://<TENANT>.sharepoint.com/") {
$admin= "https://<TENANT>-admin.sharepoint.com"
$user= "<USER>@<TENANT>.onmicrosoft.com"
$pswd= ConvertTo-SecureString "<PASSWORD>" -AsPlainText -Force
$cred0 = New-Object System.Management.Automation.PSCredential($user,$pswd)
Connect-SPOService -Url $admin -Credential $cred0
$global:cc= New-Object ` Microsoft.SharePoint.Client.ClientContext($site)
$cred1 = New-Object ` Microsoft.SharePoint.Client.SharePointOnlineCredentials($user, $pswd)
$cc.Credentials= $cred1;
}

 これが完成版。前回分からの追加は、ClientContext オブジェクトの生成だけ。CSOM にアクセスする際は、グローバルに配置した $cc を手繰って実行することになる。

ただ、CSOM の実装はかなりひねくれているうえに、(英文も含めて)まともなドキュメントが殆ど見つからない。あまり仕事にはしたくないフレームワークかもねぇ。


SharePoint Online - 2 [PowerShell]

※ 今回の記事は 「セキュリティ的にどうよ!?」 って感じの内容となっています。ここで紹介するスクリプトを使用した結果については保証しかねます。実施する際は自己責任でお願いいたします。

PowerShell を使って SharePoint Online の管理作業を進めようとすると、まずは認証作業が必須。認証と言っても、まあ大して難しい話じゃない。(管理者の)ユーザーアカウントとパスワードを入力してやるだけなんだけど、毎回同じパスワードを入力してても、間違ってしまうことがないわけじゃない。

コレを自動化しようと思って web を探してみたのだけど、完全自動化を目指すものは殆ど見つからなかった。ある程度 PowerShell を弄っているとすぐにでも思いつきそうなのに...。あまりストレスを感じていないのだろうか、それとも、それほど禁忌 に触れるものなの?

SharePoint Online への完全自動化ログオンスクリプトを一挙公開。と言っても、大して長いものじゃない

function global:SharePointOnline {
$admin= "https://<TENANT>-admin.sharepoint.com"
$user= "<USER>@<TENANT>.onmicrosoft.com"
$pswd= ConvertTo-SecureString "<PASSWORD>" -AsPlainText -Force
$cred0 = New-Object System.Management.Automation.PSCredential($user,$pswd)
Connect-SPOService -Url $admin -Credential $cred0
}

<TENANT>、<USER>、<PASSWORD> には、使用している環境に合わせた値(文字列)を設定してください。

Profile.ps1 でコレを実行し、SharePointOnline を呼び出せばログオンが完了する。このとき、認証のダイアログは表示されない。

 念のため、もう一度書きます。ここで紹介するスクリプトを使用した結果については保証しかねます。実施する際は自己責任でお願いいたします。

 


SharePoint Online - 1 [PowerShell]

タイトルが SharePoint Online でカテゴリーが PowerShell、なんの繋がりもなさそうだけど間に PowerPoint を絡ませると伝言ゲームが成立する、、、というのは冗談で、PowerShell を使って SharePoint Online でイジるのが本題。

基本的に 「同じ操作を 3 回以上繰り返すのはイヤ」 という性格なもんで、すぐに PowerShell 等のスクリプトを弄り倒して省力化を図るのが常。

SharePoint ってどんなものかよくわからないって人も少なくない。Microsoft による情報共有のための基盤の実装という事以外、私もよく解っていない。エクスプローラーではダメなの?Excel とは違うの?なぜオンライン?という疑問を抱いたまま、無料で SharePoint が試用できる Office 365 に登録してみた。

実は SharePoint(の古いバージョン) をオンプレミスに展開したことがある。その時の感想は、「重い」、「とにかく重い」、「なぜ壊れる」、「削除!!」という、ネガティブなものでしかなかった。

今は、SharePoint Online の試用版が利用できるので、多少無茶しても(ていうか無茶する気満々ですが)使い捨てできるので気楽に弄れる。ある程度のスキルを得たら、再度オンプレミスの展開にチャレンジしてみようとも思う。

SharePoint Online は Office 365 の一部として構成されている。実際にライセンスを購入した場合には、そのエディションによって利用の可/不可があるのだけれど、試用版ではほぼフルセットの機能を試すことができる。

とっかかり

SharePoint では、エクスプローラー風のファイル管理機構と Excel 風のスプレッドシートの機能を提供してくれる。まあ、エクスプローラーほど反応は良くないし、Excel ほど多機能でもない。また、名称から想像できる、共有フォルダほど気楽に使えるものでもない。

SharePoint で提供されている機能は、情報共有のための基盤となるようなものに特化しているように見える。[会社固有の...]のようなものを実現したければ、ある程度コアな世界に足を突っ込むしかないのかもしれない。

SharePoint Online の管理機能を利用するには、ある程度の準備作業が必要。次回以降、PowerShell を使った SharePoint Online のイジり方を紹介します。

 


前の10件 | - PowerShell ブログトップ