PowerShellを使って共有フォルダのアクセスログを取る

個人ブログから過去記事を移転。
**方法２**に関してはこちらのほうが良記事 → [ファイルサーバのアクセスログをPowerShellで取得する](https://qiita.com/mindwood/items/da5932845548a64a7946)

----
サーバーの共有フォルダへのアクセスがどれだけあるのか統計を取りたい。

**以下要件**

* Windowsの標準機能で実装
* 同時接続１００人程度

## 方法１：SMBを監視する
ネットワーク上にある共有フォルダなら、`SMB`のイベントを監視すればいいじゃない。
ということでプロトコルの監視。

* 利点
    * 事前の仕掛けが必要ない
* 欠点
    * 常時プロセスが動きっぱなし
    * ローカルのアクセスは把握できない

`WMIEvent`を使う方法を紹介。
`NetEvent`を使う場合は注釈参照[^1]。
永続化するにはMOF作成が必要っぽい。

```powershell
$smb_namespace = "root\Microsoft\Windows\SMB"
$smb_query = "Select * From __InstanceCreationEvent WITHIN 1 Where TargetInstance ISA 'MSFT_SmbOpenFile'"
$fs = New-Object System.IO.StreamWriter("D:\log.txt",$true,[Text.Encoding]::GetEncoding("UTF-8"))
$fs.AutoFlush = $true

#イベントが発生すると実行されるAction
$smb_event = {
    $e = $Event.SourceEventArgs.NewEvent
    $out = [DateTime]::FromFileTime($e.TIME_CREATED).ToString() + "|" + $e.TargetInstance.ClientUserName + "|" + $e.TargetInstance.Path
    $fs.WriteLine($out)
}

#「SMBLog」という名前のイベントを作成
Register-WmiEvent -Namespace $smb_namespace -Query $smb_query -SourceIdentifier "SMBLog" -Action $smb_event

#Get-EventSubscriberで動作中のイベントを確認
#Unregister-eventでイベント停止
```

## 方法２：Windowsログの監査ログを利用する
[Windowsの監査ログを使って特定のファイルアクセスを監視する](http://replication.hatenablog.com/entry/2016/08/08/010914)
上記記事を参考に監査ポリシーの設定をいじると、イベントビューアで閲覧可能なログを吐いてくれる。

* 利点
    * 常時ジョブを走らせなくていい
* 欠点
    * ログの取得が細かく取捨選択出来ないため、不要なログが貯まる。例えば、１ファイル開くだけでハンドル操作やらオープンやらのログが大量に発生する。

`XPath`で抽出条件を指定

```posh
$fromDay = -20
$days = 20
$startTime = [DateTime](Get-Date).AddDays($fromDay).ToString("yyyy/MM/dd 00:00:00")
$startUTCTime = [System.TimeZoneInfo]::ConvertTimeToUtc($startTime).ToString("yyyy-MM-ddTHH:mm:ssZ")
$endUTCTime = [System.TimeZoneInfo]::ConvertTimeToUtc($startTime.AddDays($days)).ToString("yyyy-MM-ddTHH:mm:ssZ")

#ログ抽出処理
#対象のEvent=4656
#explorerのアクセスは取得しない
$filter = @"
    Event/System/EventID='4656' and
    Event/EventData[
    Data[@Name='ProcessName']!='C:\Windows\explorer.exe'
    ] and
    Event/EventData[
    Data[@Name='ObjectType']='File'
    ] and
    Event/System/TimeCreated[@SystemTime>='$startUTCTime'] and
    Event/System/TimeCreated[@SystemTime<'$endUTCTime']
"@

#ログ抽出
try{
    Get-WinEvent -LogName security -FilterXPath $filter | % {
        $eventxml = [xml]$_.toXML()
        #名前空間を指定しないと抽出不可能
        $ns = New-Object Xml.XmlNamespaceManager $eventxml.NameTable
        $ns.AddNamespace("e", "http://schemas.microsoft.com/win/2004/08/events/event")

        $prop = @{"Time"=[DateTime]$eventxml.SelectSingleNode('e:Event/e:System/e:TimeCreated', $ns).Attributes.GetNamedItem("SystemTime").Value;
                  "User"=$eventxml.SelectSingleNode('e:Event/e:EventData/e:Data[@Name="SubjectUserName"]', $ns).InnerText;
                  "Domain"=$eventxml.SelectSingleNode('e:Event/e:EventData/e:Data[@Name="SubjectDomainName"]', $ns).InnerText;
                  "Path"=$eventxml.SelectSingleNode('e:Event/e:EventData/e:Data[@Name="ObjectName"]', $ns).InnerText;
                  "Process"=$eventxml.SelectSingleNode('e:Event/e:EventData/e:Data[@Name="ProcessName"]', $ns).InnerText}
                  return New-Object -TypeName PSObject -Property $prop
        }
}
catch {
    Write-Host "該当ログなし"
    $error | Select-Object -Last 1
}
```

[^1]: [Capture network traces with the PowerShell module NetEventPacketCapture](https://4sysops.com/archives/capture-network-traces-with-the-powershell-module-neteventpacketcapture/)