> 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/de/dev-reference/report-functions/publish-rjrbfilestostoragecontainer.md).

# Publish-RjRbFilesToStorageContainer

## Überblick

`Publish-RjRbFilesToStorageContainer` ist der Standard-Helfer für die Bereitstellung von Berichtsdateien (CSV, XLSX, ZIP, …) aus RealmJoin-Reporting-Runbooks über Azure Blob Storage. Er lädt eine oder mehrere lokale Dateien in einen Zielcontainer hoch und gibt für jeden Blob einen zeitlich begrenzten SAS-Downloadlink zurück, geeignet zur Einbindung in Berichts-E-Mails, Teams-Nachrichten oder Runbook-Ausgaben.

Hauptmerkmale:

* **Keine `Az.Storage` Abhängigkeit** — Blob-Operationen werden direkt gegen die Azure Storage REST API (Containererstellung, Upload, SAS-Token-Generierung) ausgeführt. Dadurch entfällt der bekannte Assembly-Konflikt zwischen `Az.Storage` und `ExchangeOnlineManagement` der in gemischten Reporting-Runbooks auftritt.
* **Selbstverbindend** — wenn kein `Az` -Kontext aktiv ist, ruft die Funktion transparent `Connect-RjRbAzAccount`. Ein optionales `-SubscriptionId` wechselt den Kontext vor jeder Speicheroperation.
* **Container automatisch erstellt** — wenn der Zielcontainer noch nicht existiert, wird er im Handumdrehen erstellt; ein vorhandener Container (HTTP 409) wird als Erfolg behandelt.
* **Uploads auf Basis von HttpClient** — verwendet `System.Net.Http.HttpClient` direkt, da Azure Automation's `Invoke-RestMethod` Interceptor die erforderlichen benutzerdefinierten Header (`x-ms-blob-type`) bei Binärdaten entfernt.
* **SAS-Links nur zum Lesen** — jede zurückgegebene URL wird mit dem Schlüssel des Storage Account signiert, ist auf einen einzelnen Blob beschränkt, nur über HTTPS verfügbar und gültig für `LinkExpiryDays` Tage (Standard 6).

Die zentralen Speichereinstellungen (Ressourcengruppe, Kontoname, Ablauf-Tage, Blobnamenpräfix), die von einem typischen Runbook verwendet werden, sind in der RealmJoin-Anpassungs-JSON hinterlegt und in [Runbook-Report-Einstellungen — Zustellung über Storage Account](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery). Dieses Dokument konzentriert sich darauf, die Funktion aus einem Runbook aufzurufen.

## Voraussetzungen

### Azure Storage Account

Ein vorhandener Azure Storage Account (general-purpose v2 empfohlen) ist erforderlich. Der Zielcontainer muss nicht vorab existieren — er wird bei der ersten Verwendung automatisch erstellt.

### Azure RBAC auf dem Storage Account

Die verwaltete Identität des Automation Accounts (oder der vom Runbook verwendete Dienstprinzipal) benötigt die folgenden Berechtigungen für den Storage Account oder seine Ressourcengruppe:

| Aktion                                              | Erforderlich für                                                                               |
| --------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| `Microsoft.Storage/storageAccounts/read`            | Lesen des Storage Account                                                                      |
| `Microsoft.Storage/storageAccounts/listKeys/action` | Abrufen des Kontoschlüssels, der für die SharedKey-Signatur und SAS-Generierung verwendet wird |

Die integrierte Rolle **Storage Account Contributor** deckt beides ab. **Storage Blob Data Contributor** allein ist *nicht* ausreichend, da die Funktion Anfragen mit dem Kontoschlüssel signiert, anstatt AAD-gestützte Blob-Operationen zu verwenden.

### Modulverfügbarkeit

Die Funktion erfordert das `Az.Accounts` Modul in der Runbook-Umgebung (`Get-AzContext`, `Set-AzContext`, `Connect-AzAccount`, `Invoke-AzRestMethod`). Deklarieren Sie es explizit im konsumierenden Runbook:

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

Wenn `Az.Accounts` zur Laufzeit nicht verfügbar ist, schlägt die Funktion früh mit einer klaren Fehlermeldung fehl — sie prüft vorab auf `Get-AzContext` und wirft *"Publish-RjRbFilesToStorageContainer benötigt das Modul 'Az.Accounts'. Fügen Sie #Requires -Modules @{ModuleName = 'Az.Accounts'; ModuleVersion = '5.3.4'} zum aufrufenden Runbook hinzu."* bevor ein Azure-Aufruf ausgeführt wird.

> **Warum ist `Az.Accounts` als eine `RequiredModules` -Eintrag in `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` ist absichtlich nur unter `ExternalModuleDependencies` (informativ) und *nicht* unter `RequiredModules` (erzwingt bei `Import-Module` -Zeit):
>
> * **Bezahlen Sie nur für das, was Sie verwenden.** Viele Runbooks verwenden nur Graph-basierte Helfer (z. B. `Send-RjRbReportEmail` ohne `-UseNativeGraphRequest`, oder `Invoke-RjRbRestMethodGraph`) und berühren nie ein Az.\*-Cmdlet. Das Anheben `Az.Accounts` zu `RequiredModules` würde jedes konsumierende Runbook zwingen, das Modul mitzuliefern, selbst wenn in seinem Codepfad nichts es benötigt — was die Cold-Start-Zeit in Azure Automation messbar erhöht.
> * **Vermeiden Sie Versionskonflikte.** Eine harte `RequiredModules` Einschränkung löst bei Import automatisch die Auflösung aus und kann eine bestimmte `Az.Accounts` Version nach sich ziehen, die mit den vom Runbook selbst festgelegten Versionen kollidiert (Az.\*-Untermodule sind bekanntlich sehr versionssensibel). Wenn das Runbook seine eigenen `#Requires -Modules` hält, bleibt die Versionswahl beim Aufrufer.
> * **Pro-Runbook-Verantwortung.** In Azure Automation ist der kanonische Ort zur Deklaration von Modulanforderungen auf Runbook-Ebene via `#Requires`, nicht auf Ebene des Hilfsmoduls. Das Hilfsmodul macht die Abhängigkeit informativ sichtbar (über `ExternalModuleDependencies` im Manifest) und über die obige Laufzeitprüfung, sodass Fehlkonfigurationen mit einer umsetzbaren Meldung laut fehlschlagen, anstatt einen Versionskonflikt stillschweigend zu verbergen.

`Az.Storage` ist **nicht** erforderlich und sollte nicht im selben Runbook importiert werden, um den oben genannten Assembly-Konflikt zu vermeiden.

## Schnellstart

Der minimal erforderliche Aufruf benötigt die lokalen Dateipfade, den Containernamen, die Ressourcengruppe und den Namen des Storage Account:

```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
```

Dies lädt `devices.csv` in den `reports` Container in `stcontosoreports` hoch und gibt ein Objekt mit dem Blobnamen, dem SAS-Ablaufzeitpunkt und einer sofort teilbaren Download-URL zurück, die standardmäßig 6 Tage gültig ist.

## Parameter

### Erforderlich

| Parameter            | Typ        | Beschreibung                                                                                                                                                                                                                                                                                                            |
| -------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FilePaths`          | `string[]` | Ein oder mehrere lokale Dateipfade zum Hochladen. Jeder Pfad muss auf eine vorhandene Datei verweisen (`Test-Path -PathType Leaf`); die Funktion wirft bereits vorab, wenn ein Eintrag fehlt.                                                                                                                           |
| `ContainerName`      | `string`   | Ziel-Blob-Container. Wird automatisch erstellt, wenn er nicht existiert. Muss den Azure-Container-Namensregeln entsprechen (klein geschrieben, 3–63 Zeichen, alphanumerisch + Bindestrich). Der Containername ist eine *pro Runbook* Entscheidung und wird im Runbook festgelegt, nicht in den zentralen Einstellungen. |
| `ResourceGroupName`  | `string`   | Ressourcengruppe, die den Storage Account enthält. Üblicherweise an die zentrale Einstellung gekoppelt `RJReport.AzureStorage.ResourceGroup`.                                                                                                                                                                           |
| `StorageAccountName` | `string`   | Name des Azure Storage Account. Üblicherweise an die zentrale Einstellung gekoppelt `RJReport.AzureStorage.StorageAccountName`.                                                                                                                                                                                         |

### Optional

| Parameter           | Typ      | Standard          | Beschreibung                                                                                                                                                                                                                                       |
| ------------------- | -------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SubscriptionId`    | `string` | aktueller Kontext | Azure-Abonnement, das den Storage Account hostet. Falls angegeben, `Set-AzContext -Subscription` wird vor jeder Speicheroperation aufgerufen. Lassen Sie es weg, um den aktuellen `Az` Kontext zu verwenden.                                       |
| `LinkExpiryDays`    | `int`    | `6`               | Gültigkeit des SAS-Links in Tagen. Validiert auf `[1, 3650]`. Derselbe Ablaufzeitpunkt wird bei einem einzelnen Aufruf auf alle Blobs angewendet. Üblicherweise an die zentrale Einstellung gekoppelt `RJReport.AzureStorage.LinkExpiryDays`.      |
| `AddBlobNamePrefix` | `bool`   | `$false`          | Wenn `$true`, werden die Blobnamen mit `yyyyMMdd-HHmmss-` (Zeitstempel von `Get-Date` zum Zeitpunkt des Uploads) vorangestellt, um Überschreibungen bei wiederholten Läufen zu verhindern. Der ursprüngliche Dateiname bleibt als Suffix erhalten. |

> **Hinweis:** Die Zuordnung zwischen diesen Parametern und der zentralen RealmJoin-Anpassungs-JSON (einschließlich der empfohlenen Standardwerte) ist dokumentiert in [Runbook-Report-Einstellungen — Zustellung über Storage Account](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery).

## Verwendungsbeispiele

### Empfohlenes Runbook-Muster

Dies ist das kanonische Muster, das von Reporting-Runbooks verwendet wird. Die Speicherkonfiguration wird aus der zentralen RealmJoin-Anpassung über `Use-RJInterface -Type Setting`, der Container ist pro Runbook fest codiert, und eine fehlende Konfiguration führt dazu, dass das Runbook mit einer umsetzbaren Meldung abbricht:

```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.AzureStorage.ResourceGroup" } )]
    [string] $ResourceGroupName,

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

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

Connect-RjRbAzAccount

if ((-not $ResourceGroupName) -or (-not $StorageAccountName)) {
    "## Um in einen Storage Account zu exportieren, verwenden Sie bitte die RJ Runbooks Customization"
    "## ( https://portal.realmjoin.com/settings/runbooks-customizations ) zum Konfigurieren:"
    "##   - RJReport.AzureStorage.ResourceGroup"
    "##   - RJReport.AzureStorage.StorageAccountName"
    throw "Fehlende Storage Account-Konfiguration."
}

# … die Exportdatei erzeugen …
$exportPath = "myReport.csv"

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

$uploadResult = $uploadResults[0]
"## Export erstellt."
"## Ablauf des Links: $($uploadResult.EndTime)"
$uploadResult.SASLink | Out-String
```

Einige Konventionen sollten Sie bei der Übernahme dieses Musters beibehalten:

* Die drei zentralen Einstellungen (`ResourceGroup`, `StorageAccountName`, `LinkExpiryDays`) werden als Runbook-Parameter bereitgestellt, verbunden über `Use-RJInterface -Type Setting`, aber typischerweise *verborgen* in der Runbook-Anpassung (`"Hide": true`) so, dass Endbenutzer sie nie sehen.
* Der Containername ist pro Runbook fest codiert (oft über einen `param` -Standardwert), damit Lebenszyklusrichtlinien und Zugriffssteuerungen pro Exporttyp angepasst werden können — es ist absichtlich *nicht* keine zentrale Einstellung.
* `AddBlobNamePrefix $true` ist die sichere Standardoption für periodische Exporte, die bei jedem Lauf einen festen Dateinamen erzeugen.
* Die Funktion wird im Haupt `try { … } catch { throw $_ } finally { Disconnect-AzAccount … }` Block aufgerufen, sodass Teilfehler bis zum Automation-Job durchgereicht werden und der Az-Kontext auch bei Erfolg freigegeben wird.

### Mehrere Dateien in einem Aufruf

`FilePaths` akzeptiert ein Array; jede Datei wird nacheinander hochgeladen und für jeden hochgeladenen Blob wird ein Ergebnisobjekt zurückgegeben.

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

foreach ($r in $results) {
    "Hochgeladen $($r.BlobName) — Download bis $($r.EndTime): $($r.SASLink)"
}
```

### Benutzerdefinierte Link-Laufzeit und explizites Abonnement

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

Nützlich, wenn das Runbook mehrere Abonnements umfasst oder wenn nachgelagerte Empfänger ein längeres Zeitfenster als die 6-Tage-Standardvorgabe benötigen.

### Kombination mit `Send-RjRbReportEmail`

Ein gängiges Muster ist, umfangreiche Daten in Blob Storage hochzuladen und den SAS-Link in eine Bericht-E-Mail einzubetten, sodass die E-Mail deutlich unter dem 4-MB-Graph- `sendMail` Limit bleibt:

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

$linkLine = "[Download {0}]({1}) (gültig bis {2:yyyy-MM-dd HH:mm} UTC)" -f `
    $uploaded[0].BlobName, $uploaded[0].SASLink, $uploaded[0].EndTime.ToUniversalTime()

$reportMd = @"
# Geräteinventar

Die vollständige Geräteliste ist als Download verfügbar:

$linkLine
"@

Send-RjRbReportEmail `
    -EmailFrom       $emailFrom `
    -EmailTo         'it-reports@contoso.com' `
    -Subject         "Geräteinventar — $(Get-Date -Format 'yyyy-MM-dd')" `
    -MarkdownContent $reportMd
```

Siehe [Send-RjRbReportEmail](/de/dev-reference/report-functions/send-rjrbreportemail.md) für die E-Mail-Seite dieses Musters.

## Verhalten & Fehlerbehandlung

### Vorab-Dateiprüfung

Bevor ein Azure-Aufruf ausgeführt wird, durchläuft die Funktion `FilePaths` und wirft `Datei '<path>' wurde nicht gefunden.` für den ersten fehlenden Eintrag. Dadurch werden Teil-Uploads verhindert, wenn der Aufrufer einen Tippfehler übergibt.

### Azure-Kontextauflösung

`Get-AzContext` wird zuerst geprüft. Wenn kein Kontext vorhanden ist oder der Kontext kein `Konto` (z. B. bei einer frischen Runbook-Ausführung) ruft die Funktion `Connect-RjRbAzAccount` zur Authentifizierung der verwalteten Identität auf. Wenn `-SubscriptionId` angegeben ist, `Set-AzContext -Subscription` wird anschließend aufgerufen.

### Containererstellung

Der Container wird mit einer `PUT …?restype=container` Anfrage erstellt:

* **HTTP 201** — Container erstellt.
* **HTTP 409** — Container existiert bereits; wird als Erfolg behandelt.
* **Jeder andere Status** — die Funktion wirft `Containererstellung fehlgeschlagen (<status>): <body>`.

### Upload-Fehler

Jede Datei wird über `HttpClient.SendAsync`. Ein nicht erfolgreicher Status beendet den Aufruf mit `Blob-Upload fehlgeschlagen (<status>): <body>`, einschließlich des von Azure Storage zurückgegebenen Rohfehlers. Früher hochgeladene Dateien, die bereits im selben Aufruf übertragen wurden, bleiben im Storage Account — der Aufrufer sollte den Aufruf gegebenenfalls in try/catch einbetten und eine Bereinigung ausführen, wenn Teil-Uploads nicht akzeptabel sind.

### Fehler beim Abrufen der Schlüssel

`Invoke-AzRestMethod` wird verwendet, um den ARM- `listKeys` -Endpunkt aufzurufen. Wenn der Antwortstatus etwas anderes als 200 ist, wirft die Funktion `Fehler beim Abrufen der Storage Account-Schlüssel für '<account>' in Ressourcengruppe '<rg>'. Status: <status>`. Die häufigsten Ursachen sind:

* Fehlende `Microsoft.Storage/storageAccounts/listKeys/action` bei der verwalteten Identität.
* Falscher Abonnementkontext (kombinieren mit `-SubscriptionId`).
* Tippfehler in `StorageAccountName` oder `ResourceGroupName`.
* Die zentralen Einstellungen `RJReport.AzureStorage.ResourceGroup` / `RJReport.AzureStorage.StorageAccountName` nicht konfiguriert — siehe [Runbook-Report-Einstellungen](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery).

### SAS-Token-Eigenschaften

Die generierten Token verwenden:

* `sv=2023-11-03` (signierte Version)
* `sr=b` (auf Blob beschränkt)
* `sp=r` (schreibgeschützt)
* `spr=https` (nur HTTPS)
* `st` auf 5 Minuten in der Vergangenheit gesetzt (Toleranz für Zeitabweichungen) und `se` zu `LinkExpiryDays` ab dem Zeitpunkt des Aufrufs.

Tokens werden mit dem Storage Account-Schlüssel signiert. **Jeder mit dem Link kann das Blob bis zum Ablauf herunterladen** — behandeln Sie die zurückgegebene SAS URL als Geheimnis.

## Ausgaben

Jeder erfolgreiche Upload erzeugt ein `PSCustomObject` mit folgenden Eigenschaften:

| Eigenschaft | Typ        | Beschreibung                                                                                                           |
| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------------- |
| `BlobName`  | `string`   | Der endgültige Blob-Name im Container, einschließlich des Zeitstempel-Präfixes, falls `AddBlobNamePrefix` ist `$true`. |
| `EndTime`   | `datetime` | Lokale SAS-Ablaufzeit (auch in der URL als UTC codiert).                                                               |
| `SASLink`   | `string`   | Vollständig qualifizierte HTTPS-Download-URL mit eingebettetem SAS Token.                                              |

Die Ergebnisse werden in derselben Reihenfolge zurückgegeben wie `FilePaths`. Auch wenn nur eine einzelne Datei hochgeladen wird, ist der Rückgabewert ein Array — indizieren Sie es (`$results[0]`) oder iterieren Sie mit `foreach` statt es als Skalar zu behandeln.

## Siehe auch

* [Runbook-Report-Einstellungen — Zustellung über Storage Account](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery) — zentrale Konfiguration des Storage Accounts, der Link-Ablaufzeit und des von Reporting-Runbooks verwendeten Blob-Namenspräfixes.
* [Send-RjRbReportEmail](/de/dev-reference/report-functions/send-rjrbreportemail.md) — begleitende Hilfsfunktion zum Versenden von Berichten per E-Mail; wird häufig mit dieser Funktion kombiniert, um die E-Mail-Nutzlast klein zu halten.
* Microsoft Docs: [Autorisierung mit Shared Key](https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key) — vom Hilfsprogramm verwendetes Signaturschema.
* Microsoft Docs: [Einen Service SAS erstellen](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) — SAS Token-Format, das zurückgegeben wird in `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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
