> 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

## Übersicht

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

Wesentliche Merkmale:

* **Nein `Az.Storage` Abhängigkeit** — Blob-Operationen werden direkt gegen die Azure Storage REST API ausgeführt (Container-Erstellung, Upload, SAS Token-Generierung). Dadurch wird 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`auf. Ein optionales `-SubscriptionId` wechselt vor jeder Speicheroperation den Kontext.
* **Container automatisch erstellt** — wenn der Ziel-Container noch nicht existiert, wird er bei Bedarf erstellt; ein vorhandener Container (HTTP 409) wird als Erfolg behandelt.
* **HttpClient-basierte Uploads** — verwendet `System.Net.Http.HttpClient` direkt, da der Azure Automation's `Invoke-RestMethod` Interceptor die erforderlichen benutzerdefinierten Header (`x-ms-blob-type`) bei binären Bodies entfernt.
* **Schreibgeschützte SAS-Links** — jede zurückgegebene URL ist mit dem Storage Account-Schlüssel signiert, auf ein einzelnes Blob beschränkt, nur per HTTPS gültig und gültig für `LinkExpiryDays` Tage (Standard: 6).

Die zentralen Speichereinstellungen (Ressourcengruppe, Kontoname, Ablauf-Tage, Blob-Namen-Präfix), die von einem typischen Runbook verwendet werden, sind in der RealmJoin-Anpassungs-JSON hinterlegt und werden dokumentiert in [Runbook-Berichtseinstellungen — Storage Account Delivery](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery). Dieses Dokument konzentriert sich auf den Aufruf der Funktion aus einem Runbook.

## Voraussetzungen

### Azure Storage Account

Ein vorhandener Azure Storage Account (general-purpose v2 empfohlen) ist erforderlich. Der Ziel-Container 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 Service Principal) benötigt auf dem Storage Account oder seiner Ressourcengruppe die folgenden Berechtigungen:

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

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

### Modulverbindung

Die Funktion benötigt 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` ist zur Laufzeit nicht verfügbar, schlägt die Funktion mit einer klaren Fehlermeldung sofort fehl — sie prüft im Voraus auf `Get-AzContext` im Voraus und wirft *"Publish-RjRbFilesToStorageContainer benötigt das Modul 'Az.Accounts'. Fügen Sie #Requires -Modules @{ModuleName = 'Az.Accounts'; ModuleVersion = '5.3.4'} dem aufrufenden Runbook hinzu."* bevor ein Azure-Aufruf erfolgt.

> **Warum ist `Az.Accounts` nicht als ein `RequiredModules` Eintrag in `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` ist absichtlich nur unter `ExternalModuleDependencies` (informativ) und *zu* unter `RequiredModules` (erzwungen bei `Import-Module` Zeitpunkt):
>
> * **Zahlen Sie nur für das, was Sie nutzen.** Viele Runbooks verwenden nur Graph-basierte Helfer (z. B. `Send-RjRbReportEmail` ohne `-UseNativeGraphRequest`, oder `Invoke-RjRbRestMethodGraph`) und verwenden nie ein Az.\*-Cmdlet. Ein Hochstufen von `Az.Accounts` zu `RequiredModules` würde jedes konsumierende Runbook dazu zwingen, das Modul mitzuliefern, selbst wenn nichts im Codepfad es benötigt — was die Cold-Start-Zeit in Azure Automation messbar erhöht.
> * **Versionskonflikte vermeiden.** Eine harte `RequiredModules` Anforderung löst beim Import eine automatische Auflösung aus und kann eine bestimmte `Az.Accounts` Version nachziehen, die mit dem kollidiert, was das Runbook selbst festlegt (Az.\*-Untermodule sind bekanntermaßen stark versionssensitiv). Wenn das Runbook seine eigene `#Requires -Modules` Version auswählt, bleibt die Versionsentscheidung beim Aufrufer.
> * **Pro-Runbook-Autonomie.** In Azure Automation ist der kanonische Ort für die Deklaration von Modulanforderungen auf Runbook-Ebene über `#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 eine Fehlkonfiguration mit einer handlungsrelevanten Nachricht laut scheitert, statt einen Versionskonflikt stillschweigend zu verdecken.

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

## Schnellstart

Der minimale Aufruf benötigt die lokalen Dateipfad(e), den Container-Namen, die Ressourcengruppe und den Storage Account-Namen:

```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 Blob-Namen, dem Ablaufzeitpunkt des SAS 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 im Voraus, wenn ein Eintrag fehlt.                                                                                                                                  |
| `ContainerName`      | `string`   | Ziel-Blob-Container. Wird automatisch erstellt, wenn er nicht existiert. Muss den Azure-Namensregeln für Container entsprechen (Kleinschreibung, 3–63 Zeichen, alphanumerisch + Bindestrich). Der Container-Name 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 Accounts. Ü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. Wenn 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. Wird validiert auf `[1, 3650]`. Derselbe Ablaufzeitpunkt wird in einem einzelnen Aufruf auf alle Blobs angewendet. Üblicherweise an die zentrale Einstellung gekoppelt `RJReport.AzureStorage.LinkExpiryDays`. |
| `AddBlobNamePrefix` | `bool`   | `$false`          | Wenn `$true`, werden Blob-Namen mit `yyyyMMdd-HHmmss-` (Zeitstempel von `Get-Date` zum Zeitpunkt des Uploads) vorangestellt, um Überschreibungen bei wiederholten Läufen zu vermeiden. 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-Berichtseinstellungen — Storage Account Delivery](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery).

## Anwendungsbeispiele

### 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 verdrahtet, und eine fehlende Konfiguration führt dazu, dass das Runbook mit einer handlungsrelevanten Nachricht 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 RJ Runbooks Customization"
    "## ( https://portal.realmjoin.com/settings/runbooks-customizations ) zur Konfiguration von:"
    "##   - 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, die es bei der Übernahme dieses Musters beizubehalten gilt:

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

### Mehrere Dateien in einem Aufruf

`FilePaths` akzeptiert ein Array; jede Datei wird sequenziell hochgeladen und für jedes hochgeladene 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) — herunterladen 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 den 6-Tage-Standard benötigen.

### Kombination mit `Send-RjRbReportEmail`

Ein gängiges Muster besteht darin, umfangreiche Daten in Blob Storage hochzuladen und den SAS-Link in eine Report-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 = "[Herunterladen {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ätebestand

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

$linkLine
"@

Send-RjRbReportEmail `
    -EmailFrom       $emailFrom `
    -EmailTo         'it-reports@contoso.com' `
    -Subject         "Gerätebestand — $(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 erfolgt, iteriert die Funktion über `FilePaths` und wirft `Datei '<path>' wurde nicht gefunden.` für den ersten fehlenden Eintrag. Dadurch werden Teil-Uploads verhindert, wenn der Aufrufer sich vertippt.

### Azure-Kontextauflösung

`Get-AzContext` wird zuerst geprüft. Wenn kein Kontext vorhanden ist oder der Kontext kein `Account` (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.

### Container-Erstellung

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

* **HTTP 201** — Container erstellt.
* **HTTP 409** — Container existiert bereits; wird als Erfolg behandelt.
* **Jeder andere Status** — die Funktion wirft `Container-Erstellung 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. Zuvor im selben Aufruf bereits hochgeladene Dateien verbleiben im Storage Account — der Aufrufer sollte den Aufruf möglicherweise in ein 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 `Abrufen der Storage Account-Schlüssel für '<account>' in Ressourcengruppe '<rg>' fehlgeschlagen. Status: <status>`. Die häufigsten Ursachen sind:

* Fehlendes `Microsoft.Storage/storageAccounts/listKeys/action` auf 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 erzeugten Tokens 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 Zeitabweichung) 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 kodiert).                                                               |
| `SASLink`   | `string`   | Vollqualifizierte HTTPS-Download-URL mit eingebettetem SAS Token.                                                      |

Die Ergebnisse werden in derselben Reihenfolge zurückgegeben wie `FilePaths`. Auch beim Hochladen einer einzelnen Datei ist der Rückgabewert ein Array — greifen Sie per Index darauf zu (`$results[0]`) oder iterieren Sie mit `foreach` statt es als Skalar zu behandeln.

## Siehe auch

* [Runbook-Berichtseinstellungen — Storage Account Delivery](/de/automatisierung/runbooks/runbook-report-settings.md#storage-account-delivery) — zentrale Konfiguration des Storage Accounts, der Link-Ablaufzeit und des Blob-Namen-Präfixes, die von Reporting-Runbooks verwendet werden.
* [Send-RjRbReportEmail](/de/dev-reference/report-functions/send-rjrbreportemail.md) — begleitender Helfer zum Versand von Berichten per E-Mail; wird häufig mit dieser Funktion kombiniert, um die E-Mail-Nutzlast klein zu halten.
* Microsoft Docs: [Mit Shared Key autorisieren](https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key) — vom Helfer verwendetes Signaturschema.
* Microsoft Docs: [Einen Service SAS erstellen](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) — SAS Token-Format zurückgegeben 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.
