> For the complete documentation index, see [llms.txt](https://docs.realmjoin.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.realmjoin.com/ja/dev-reference/report-functions/publish-rjrbfilestostoragecontainer.md).

# Publish-RjRbFilesToStorageContainer

Azure Automation runbook から Azure Storage コンテナーにローカル ファイルをアップロードし、期限付き SAS ダウンロード リンクを返します。

## 概要

`Publish-RjRbFilesToStorageContainer` は、RealmJoin のレポート用 runbook から Azure Blob Storage 経由でレポート ファイル（CSV、XLSX、ZIP、…）を配信するための標準ヘルパーです。1 つ以上のローカル ファイルを対象コンテナーにアップロードし、各 blob ごとに有効期限付きの SAS ダウンロード リンクを返します。これは、レポート メール、Teams メッセージ、または runbook の出力に含めるのに適しています。

主な特徴:

* **なし `Az.Storage` 依存関係** — blob 操作は Azure Storage REST API に対して直接実行されます（コンテナー作成、アップロード、SAS トークン生成）。これにより、次の間で知られているアセンブリ競合が解消されます `Az.Storage` と `ExchangeOnlineManagement` 。これは、混在したレポート用 runbook で発生します。
* **自己接続** — もし `Az` のコンテキストがアクティブでない場合、関数は透過的に `Connect-RjRbAzAccount`を呼び出します。オプションの `-SubscriptionId` は、いかなるストレージ操作の前にもコンテキストを切り替えます。
* **コンテナーの自動作成** — 対象コンテナーがまだ存在しない場合はその場で作成され、既存コンテナー（HTTP 409）は成功として扱われます。
* **HttpClient ベースのアップロード** — 使用します: `System.Net.Http.HttpClient` を直接。なぜなら Azure Automation の `Invoke-RestMethod` インターセプターが必要なカスタム ヘッダー（`x-ms-blob-type`）をバイナリ本文から除去してしまうためです。
* **読み取り専用 SAS リンク** — 返される各 URL は Storage Account キーで署名され、単一の blob にスコープされ、HTTPS のみで、次の期間有効です: `LinkExpiryDays` 日間（既定値 6）です。

典型的な runbook が使用する中央のストレージ設定（resource group、account name、有効期限日数、blob 名プレフィックス）は RealmJoin のカスタマイズ JSON にあり、次で文書化されています: [Runbook レポート設定 — Storage Account 配信](/ja/zi-dong-hua/runbooks/runbook-report-settings.md#storage-account-delivery)。このドキュメントでは、runbook からこの関数を呼び出す方法に焦点を当てます。

## 前提条件

### Azure Storage Account

既存の Azure Storage Account（汎用 v2 推奨）が必要です。対象コンテナーは事前に存在している必要はなく、初回使用時に自動的に作成されます。

### ストレージ アカウントに対する Azure RBAC

Automation Account のマネージド ID（または runbook で使用する Service Principal）には、ストレージ アカウントに対して次の権限が必要です:

| 操作                                                  | 必要な用途                                |
| --------------------------------------------------- | ------------------------------------ |
| `Microsoft.Storage/storageAccounts/read`            | ストレージ アカウントの読み取り                     |
| `Microsoft.Storage/storageAccounts/listKeys/action` | SharedKey 署名と SAS 生成に使用するアカウント キーの取得 |

組み込みロールを割り当てます **Reader and Data Access** ストレージ アカウントに対して: これら両方をカバーする最小の組み込みロールです。コンテナー作成、アップロード、SAS 生成はアカウント キーで認可されるため、追加の Azure RBAC 権限は不要です。 **Storage Account Contributor** でも問題ありませんが、ストレージ アカウントの管理と削除も可能になります。 **Storage Blob Data Contributor** のみでは *不十分です* なぜなら、この関数は AAD ベースの blob 操作ではなく、アカウント キーで要求に署名するためです。

この関数はアカウント キーで認証するため、ストレージ アカウントでは共有キー アクセスを許可しておく必要があります（`allowSharedKeyAccess` は `false`）。このキーはストレージ アカウント内のすべてのデータにアクセスできるため、レポート専用のストレージ アカウントを推奨します。

### モジュール接続

この関数には `Az.Accounts` モジュールが runbook 環境に必要です（`Get-AzContext`, `Set-AzContext`, `Connect-AzAccount`, `Invoke-AzRestMethod`）。利用側 runbook で明示的に宣言してください:

```powershell
#Requires -Modules @{ModuleName = "RealmJoin.RunbookHelper"; ModuleVersion = "0.8.6" }
#Requires -Modules @{ModuleName = "Az.Accounts"; ModuleVersion = "5.3.4" }
```

もし `Az.Accounts` 実行時に利用できない場合、関数は明確なエラーメッセージで即座に失敗します — まず `Get-AzContext` を事前に確認し、次をスローします: *"Publish-RjRbFilesToStorageContainer には 'Az.Accounts' モジュールが必要です。呼び出し元の runbook に #Requires -Modules @{ModuleName = 'Az.Accounts'; ModuleVersion = '5.3.4'} を追加してください。"* いかなる Azure 呼び出しの前にも。

> **なぜ `Az.Accounts` として宣言されていないのですか `RequiredModules` エントリとして `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` は、意図的に次の下にのみ記載されています: `ExternalModuleDependencies` （情報用）として、また *不十分です* 次の下に `RequiredModules` （で強制される `Import-Module` 時）:
>
> * **使用した分だけ支払う、という考え方です。** 多くの runbook は Graph ベースのヘルパーだけを使います（例: `Send-RjRbReportEmail` を使わずに `-UseNativeGraphRequest`、または `Invoke-RjRbRestMethodGraph`）で、Az.\* cmdlet には一切触れません。これを `Az.Accounts` に昇格させると `RequiredModules` 利用側のすべての runbook に、そのコードパスで不要な場合でもモジュールを含めることになり、Azure Automation のコールドスタート時間が測定可能なほど増加します。
> * **バージョンの衝突を避けるためです。** 厳密な `RequiredModules` 制約はインポート時に自動解決を引き起こし、特定の `Az.Accounts` バージョンを引き込んで、runbook 自体が固定している内容と衝突する可能性があります（Az.\* のサブモジュールは非常にバージョン依存が強いことで知られています）。runbook 自身に独自の `#Requires -Modules` を宣言させることで、バージョン選択を呼び出し元に任せられます。
> * **runbook ごとの権限。** Azure Automation では、モジュール要件を宣言する正規の場所は runbook レベルの `#Requires`であり、ヘルパーモジュール レベルではありません。ヘルパーモジュールは依存関係を情報として（ `ExternalModuleDependencies` マニフェスト内で）および上記の実行時チェックで示すため、設定ミスはバージョン競合を静かに隠すのではなく、実行可能なメッセージで明確に失敗します。

`Az.Storage` は **不十分です** 必要であり、前述のアセンブリ競合を避けるため同じ runbook ではインポートしないでください。

## クイック スタート

最低限必要な呼び出しには、ローカル ファイル パス、コンテナー名、resource group、ストレージ アカウント名が必要です:

```powershell
$csvPath = Join-Path $env:TEMP 'devices.csv'
$exportData | Export-Csv -Path $csvPath -NoTypeInformation -Encoding UTF8

$results = Publish-RjRbFilesToStorageContainer `
    -FilePaths          $csvPath `
    -ContainerName      'reports' `
    -ResourceGroupName  'rg-reports' `
    -StorageAccountName 'stcontosoreports'

$results | Format-Table BlobName, EndTime, SASLink
```

これは `devices.csv` に `reports` コンテナーへ `stcontosoreports` にアップロードし、blob 名、SAS の有効期限タイムスタンプ、そして既定の 6 日間有効な、そのまま共有できるダウンロード URL を含む 1 つのオブジェクトを返します。

## パラメーター

### 必須

| パラメーター               | 型          | 説明                                                                                                                                           |
| -------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| `FilePaths`          | `string[]` | アップロードするローカル ファイル パスを 1 つ以上指定します。各パスは既存のファイルを指している必要があります（`Test-Path -PathType Leaf`）。いずれか 1 つでも欠けている場合、関数は事前に例外をスローします。                     |
| `ContainerName`      | `string`   | 対象の blob コンテナーです。存在しない場合は自動的に作成されます。Azure のコンテナー命名規則（小文字、3〜63 文字、英数字 + ハイフン）に従う必要があります。コンテナー名は *runbook ごとの* 判断であり、中央設定ではなく runbook で設定されます。 |
| `ResourceGroupName`  | `string`   | ストレージ アカウントを含む resource group です。通常は中央設定 `RJReport.StorageAccount.ResourceGroup`.                                                            |
| `StorageAccountName` | `string`   | Azure Storage Account の名前です。通常は中央設定 `RJReport.StorageAccount.StorageAccountName`.                                                            |

### オプション

| パラメーター              | 型        | 既定        | 説明                                                                                                                                   |
| ------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `SubscriptionId`    | `string` | 現在のコンテキスト | ストレージ アカウントをホストする Azure サブスクリプションです。指定した場合、 `Set-AzContext -Subscription` は、ストレージ操作の前に呼び出されます。省略すると現在の `Az` コンテキストが使われます。            |
| `LinkExpiryDays`    | `int`    | `6`       | SAS リンクの有効日数です。次の範囲で検証されます: `[1, 3650]`。同じ有効期限タイムスタンプが 1 回の呼び出し内のすべての blob に適用されます。通常は中央設定 `RJReport.StorageAccount.LinkExpiryDays`. |
| `AddBlobNamePrefix` | `bool`   | `$false`  | 〜の場合 `$true`、blob 名の先頭に `yyyyMMdd-HHmmss-` （次のタイムスタンプを使用: `Get-Date` アップロード時）を付与し、繰り返し実行時の上書きを防ぎます。元のファイル名はサフィックスとして保持されます。          |

> **注:** これらのパラメーターと中央の RealmJoin カスタマイズ JSON（推奨既定値を含む）との対応は次で文書化されています: [Runbook レポート設定 — Storage Account 配信](/ja/zi-dong-hua/runbooks/runbook-report-settings.md#storage-account-delivery).

## 使用例

### 推奨される runbook パターン

これはレポート用 runbook で使用される標準パターンです。ストレージ構成は、中央の RealmJoin カスタマイズから次を介して取得されます: `Use-RJInterface -Type Setting`、コンテナーは runbook ごとにハードコードされており、設定が欠けている場合は、実行可能なメッセージとともに runbook が中止されます:

```powershell
#Requires -Modules @{ModuleName = "RealmJoin.RunbookHelper"; ModuleVersion = "0.8.6" }
#Requires -Modules @{ModuleName = "Az.Accounts"; ModuleVersion = "5.3.4" }

param(
    [string] $ContainerName = "my-runbook-output",

    [ValidateScript( { Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; Use-RJInterface -Type Setting -Attribute "RJReport.StorageAccount.ResourceGroup" } )]
    [string] $ResourceGroupName,

    [ValidateScript( { Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; Use-RJInterface -Type Setting -Attribute "RJReport.StorageAccount.StorageAccountName" } )]
    [string] $StorageAccountName,

    [ValidateScript( { Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process; Use-RJInterface -Type Setting -Attribute "RJReport.StorageAccount.LinkExpiryDays" } )]
    [ValidateRange(1, 3650)]
    [int] $LinkExpiryDays = 6
)

Connect-RjRbAzAccount

if ((-not $ResourceGroupName) -or (-not $StorageAccountName)) {
    "## ストレージ アカウントにエクスポートするには、RJ Runbooks Customization を使用してください"
    "## （ https://portal.realmjoin.com/settings/runbooks-customizations ）で次を構成してください:"
    "##   - RJReport.StorageAccount.ResourceGroup"
    "##   - RJReport.StorageAccount.StorageAccountName"
    throw "Storage Account 設定が見つかりません。"
}

# … エクスポート ファイルを作成 …
$exportPath = "myReport.csv"

$uploadResults = Publish-RjRbFilesToStorageContainer `
    -FilePaths          @($exportPath) `
    -ContainerName      $ContainerName `
    -ResourceGroupName  $ResourceGroupName `
    -StorageAccountName $StorageAccountName `
    -LinkExpiryDays     $LinkExpiryDays `
    -AddBlobNamePrefix  $true

$uploadResult = $uploadResults[0]
"## エクスポートが作成されました。"
"## リンクの有効期限: $($uploadResult.EndTime)"
$uploadResult.SASLink | Out-String
```

このパターンを採用する際に守っておくとよい慣例がいくつかあります:

* 3 つの中央設定（`ResourceGroup`, `StorageAccountName`, `LinkExpiryDays`）は、次を通じて連携された runbook パラメーターとして公開されます: `Use-RJInterface -Type Setting`、ただし通常は *非表示* runbook カスタマイズ内で（`"Hide": true`）として設定されるため、エンドユーザーには表示されません。
* コンテナー名は runbook ごとにハードコードされます（多くの場合、次の `param` の既定値を介して）。これにより、ライフサイクル ポリシーとアクセス制御をエクスポート種別ごとに調整できます — これは意図的に *不十分です* 中央設定ではありません。
* `AddBlobNamePrefix $true` 毎回固定のファイル名を生成する定期エクスポートでは、安全な既定値です。
* 関数は runbook のメイン `try { … } catch { throw $_ } finally { Disconnect-AzAccount … }` ブロック内で呼び出されるため、部分的な失敗は Automation ジョブへ伝播し、成功時でも Az コンテキストは解放されます。

### 1 回の呼び出しで複数ファイル

`FilePaths` 配列を受け取ります。各ファイルは順番にアップロードされ、アップロードされた各 blob ごとに結果オブジェクトが返されます。

```powershell
$results = Publish-RjRbFilesToStorageContainer `
    -FilePaths          @($csvPath, $xlsxPath) `
    -ContainerName      'reports' `
    -ResourceGroupName  $ResourceGroupName `
    -StorageAccountName $StorageAccountName

foreach ($r in $results) {
    "アップロード済み $($r.BlobName) — $($r.EndTime) までダウンロード可能: $($r.SASLink)"
}
```

### カスタム リンク有効期間と明示的なサブスクリプション

```powershell
Publish-RjRbFilesToStorageContainer `
    -FilePaths          $exportPaths `
    -ContainerName      'quarterly-reports' `
    -ResourceGroupName  $ResourceGroupName `
    -StorageAccountName $StorageAccountName `
    -SubscriptionId     '00000000-0000-0000-0000-000000000000' `
    -LinkExpiryDays     30
```

runbook が複数のサブスクリプションにまたがる場合や、下流の受信者が 6 日の既定値より長い有効期間を必要とする場合に便利です。

### と組み合わせる場合 `Send-RjRbReportEmail`

よくあるパターンは、容量の大きいデータを blob ストレージにアップロードし、SAS リンクをレポートメールに埋め込んで、メールサイズを 4 MB の Graph `sendMail` 制限より十分小さく保つことです:

```powershell
$uploaded = Publish-RjRbFilesToStorageContainer `
    -FilePaths          $csvPath `
    -ContainerName      'reports' `
    -ResourceGroupName  $ResourceGroupName `
    -StorageAccountName $StorageAccountName `
    -AddBlobNamePrefix  $true

$linkLine = "[ダウンロード {0}]({1})（{2:yyyy-MM-dd HH:mm} UTC まで有効）" -f `
    $uploaded[0].BlobName, $uploaded[0].SASLink, $uploaded[0].EndTime.ToUniversalTime()

$reportMd = @"
# デバイス一覧

完全なデバイス一覧はダウンロードできます:

$linkLine
"@

Send-RjRbReportEmail `
    -EmailFrom       $emailFrom `
    -EmailTo         'it-reports@contoso.com' `
    -Subject         "デバイス一覧 — $(Get-Date -Format 'yyyy-MM-dd')" `
    -MarkdownContent $reportMd
```

詳細は [Send-RjRbReportEmail](/ja/dev-reference/report-functions/send-rjrbreportemail.md) このパターンのメール側については、こちらを参照してください。

## 動作とエラーハンドリング

### 事前のファイル検証

いかなる Azure 呼び出しの前にも、関数は `FilePaths` を順に確認し、次をスローします: `ファイル '<path>' が見つかりませんでした。` 最初に見つからないエントリについて。これにより、呼び出し側が誤記を渡した場合でも部分的なアップロードを防げます。

### Azure コンテキストの解決

`Get-AzContext` まず確認されます。コンテキストが存在しないか、コンテキストに `Account` （たとえば新規の runbook 実行）、 `Connect-RjRbAzAccount` マネージド ID を認証するために呼び出します。もし `-SubscriptionId` が指定されている場合、 `Set-AzContext -Subscription` 次に呼び出されます。

### コンテナー作成

コンテナーは次を使用して作成されます: `PUT …?restype=container` 要求:

* **HTTP 201** — コンテナーが作成されました。
* **HTTP 409** — コンテナーは既に存在します。成功として扱われます。
* **その他のステータス** — 関数は次をスローします: `コンテナーの作成に失敗しました（<status>）: <body>`。HTTP 403 に `KeyBasedAuthenticationNotPermitted` が含まれる場合、ストレージ アカウントで共有キー アクセスが無効になっています。

### アップロード失敗

各ファイルは次を介してアップロードされます: `HttpClient.SendAsync`。成功以外のステータスの場合、呼び出しは次で終了します: `blob のアップロードに失敗しました（<status>）: <body>`。Azure Storage が返した生のエラーを含みます。同じ呼び出しですでにアップロード済みの先行ファイルはストレージ アカウントに残ります — 部分的なアップロードが許容できない場合は、呼び出しを try/catch で囲み、クリーンアップを実行するとよいでしょう。

### キー取得の失敗

`Invoke-AzRestMethod` を使用して ARM `listKeys` エンドポイントを呼び出します。応答ステータスが 200 以外の場合、関数は次をスローします: `resource group '<rg>' 内の '<account>' のストレージ アカウント キーの取得に失敗しました。ステータス: <status>`。最も一般的な原因は次のとおりです:

* 不足 `Microsoft.Storage/storageAccounts/listKeys/action` している場合のマネージド ID の権限です。
* 間違ったサブスクリプション コンテキスト（次と組み合わせて `-SubscriptionId`).
* の টাইपो `StorageAccountName` または `ResourceGroupName`.
* 中心設定 `RJReport.StorageAccount.ResourceGroup` / `RJReport.StorageAccount.StorageAccountName` 未構成 — を参照 [Runbook レポート設定](/ja/zi-dong-hua/runbooks/runbook-report-settings.md#storage-account-delivery).

### SAS Token の特性

生成されるトークンは次を使用します:

* `sv=2023-11-03` (署名済みバージョン)
* `sr=b` (Blob スコープ)
* `sp=r` (読み取り専用)
* `spr=https` (HTTPS のみ)
* `st` 5 分前に設定され（クロックずれの許容範囲）および `se` に昇格させると `LinkExpiryDays` 呼び出し時刻から。

トークンは Storage Account キーで署名されます。 **リンクを持つ人は、期限切れまで Blob をダウンロードできます** — 返された SAS URL は秘密情報として扱ってください。

## 出力

各正常なアップロードは次を生成します: `PSCustomObject` 次のプロパティを持ちます:

| プロパティ      | 型          | 説明                                                                             |
| ---------- | ---------- | ------------------------------------------------------------------------------ |
| `BlobName` | `string`   | コンテナー内の最終的な Blob 名。タイムスタンプのプレフィックスがある場合はそれも含みます `AddBlobNamePrefix` は `$true`. |
| `EndTime`  | `datetime` | ローカル時刻の SAS の有効期限（URL には UTC としてもエンコードされます）。                                   |
| `SASLink`  | `string`   | 埋め込まれた SAS Token を含む完全修飾 HTTPS ダウンロード URL。                                     |

結果は同じ順序で返されます `FilePaths`。1 つのファイルをアップロードする場合でも、戻り値は配列です — それをインデックス指定してください (`$results[0]`) または foreach で反復してください `foreach` スカラーとして扱うのではなく。

## 関連項目

* [Runbook レポート設定 — Storage Account 配信](/ja/zi-dong-hua/runbooks/runbook-report-settings.md#storage-account-delivery) — レポート用 Runbook で使用される Storage Account、リンクの有効期限、および Blob 名プレフィックスの中央設定。
* [Send-RjRbReportEmail](/ja/dev-reference/report-functions/send-rjrbreportemail.md) — レポートをメールで配信するための補助ヘルパー。メールのペイロードを小さく保つため、この関数と組み合わせて使われることが多い。
* Microsoft Docs: [Shared Key で認証する](https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key) — ヘルパーで使用される署名方式。
* Microsoft Docs: [サービス SAS を作成する](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) — で返される SAS Token 形式 `SASLink`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.realmjoin.com/ja/dev-reference/report-functions/publish-rjrbfilestostoragecontainer.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
