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

# Publish-RjRbFilesToStorageContainer

Télécharge des fichiers locaux vers un conteneur Azure Storage depuis des runbooks Azure Automation et renvoie des liens de téléchargement SAS à durée limitée.

## Vue d’ensemble

`Publish-RjRbFilesToStorageContainer` est l’assistant standard pour la livraison de fichiers de rapport (CSV, XLSX, ZIP, …) depuis les runbooks de reporting RealmJoin via Azure Blob Storage. Il téléverse un ou plusieurs fichiers locaux vers un conteneur cible et renvoie pour chaque blob un lien de téléchargement SAS à durée limitée, adapté à l’inclusion dans des e-mails de rapport, des messages Teams ou des sorties de runbook.

Caractéristiques principales :

* **Aucune `Az.Storage` dépendance** — les opérations sur les blobs sont effectuées directement via l’API REST Azure Storage (création de conteneur, téléversement, génération de SAS Token). Cela élimine le conflit bien connu entre les assemblys `Az.Storage` et `ExchangeOnlineManagement` qui apparaît dans les runbooks de reporting mixtes.
* **Connexion automatique** — si aucun `Az` contexte n’est actif, la fonction appelle de manière transparente `Connect-RjRbAzAccount`. Un `-SubscriptionId` optionnel bascule le contexte avant toute opération de stockage.
* **Conteneur créé automatiquement** — si le conteneur cible n’existe pas encore, il est créé à la volée ; un conteneur existant (HTTP 409) est traité comme un succès.
* **Téléversements basés sur HttpClient** — utilise `System.Net.Http.HttpClient` directement, car le `Invoke-RestMethod` intercepteur d’Azure Automation supprime les en-têtes personnalisés requis (`x-ms-blob-type`) avec des corps binaires.
* **Liens SAS en lecture seule** — chaque URL renvoyée est signée avec la clé du compte de stockage, limitée à un seul blob, en HTTPS uniquement, et valide pendant `LinkExpiryDays` jours (par défaut 6).

Les paramètres centraux de stockage (groupe de ressources, nom du compte, nombre de jours d’expiration, préfixe du nom de blob) utilisés par un runbook typique se trouvent dans le JSON de personnalisation RealmJoin et sont documentés dans [Paramètres du rapport de runbook — Livraison du Storage Account](/fr/automatisation/runbooks/runbook-report-settings.md#storage-account-delivery). Ce document se concentre sur l’appel de la fonction depuis un runbook.

## Prérequis

### Azure Storage Account

Un Azure Storage Account existant (généraliste v2 recommandé) est requis. Le conteneur cible n’a pas besoin d’exister au préalable — il est créé automatiquement lors de la première utilisation.

### Azure RBAC sur le compte de stockage

L’identité managée du compte Automation (ou le Service Principal utilisé par le runbook) a besoin des autorisations suivantes sur le compte de stockage ou son groupe de ressources :

| Action                                              | Requis pour                                                                                   |
| --------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `Microsoft.Storage/storageAccounts/read`            | Lecture du compte de stockage                                                                 |
| `Microsoft.Storage/storageAccounts/listKeys/action` | Récupération de la clé du compte utilisée pour la signature SharedKey et la génération de SAS |

Le rôle intégré **Storage Account Contributor** couvre les deux. **Storage Blob Data Contributor** à lui seul n’est *pas* suffisant, car la fonction signe les requêtes avec la clé du compte plutôt que d’utiliser des opérations blob basées sur AAD.

### Connectivité du module

La fonction nécessite le module `Az.Accounts` dans l’environnement du runbook (`Get-AzContext`, `Set-AzContext`, `Connect-AzAccount`, `Invoke-AzRestMethod`). Déclarez-le explicitement dans le runbook consommateur :

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

Si `Az.Accounts` n’est pas disponible à l’exécution, la fonction échoue immédiatement avec un message d’erreur clair — elle vérifie `Get-AzContext` à l’avance et lève *"Publish-RjRbFilesToStorageContainer requires the 'Az.Accounts' module. Add #Requires -Modules @{ModuleName = 'Az.Accounts'; ModuleVersion = '5.3.4'} to the calling runbook."* avant toute appel Azure.

> **Pourquoi n’est-ce pas `Az.Accounts` déclaré comme `RequiredModules` dans `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` est intentionnellement listé uniquement sous `ExternalModuleDependencies` (informatif) et *pas* sous `RequiredModules` (imposé au moment de `Import-Module` ?) :
>
> * **Payez seulement pour ce que vous utilisez.** De nombreux runbooks n’utilisent que des assistants basés sur Graph (par ex. `Send-RjRbReportEmail` sans `-UseNativeGraphRequest`, ou `Invoke-RjRbRestMethodGraph`) et n’utilisent jamais de cmdlet Az.\*. Faire passer `Az.Accounts` à `RequiredModules` obligerait chaque runbook consommateur à embarquer le module même si rien dans son chemin d’exécution n’en a besoin — ce qui augmente sensiblement le temps de démarrage à froid dans Azure Automation.
> * **Évitez les querelles de versions.** Une `RequiredModules` contrainte stricte déclenche la résolution automatique au moment de l’import et peut entraîner l’inclusion d’une version spécifique qui entre en conflit avec celle épinglée par le runbook lui-même (les sous-modules Az.\* sont notoirement sensibles aux versions). Laisser le runbook déclarer son propre `Az.Accounts` #Requires -Modules `#Requires -Modules` laisse le choix de la version à l’appelant.
> * **Autorité par runbook.** Dans Azure Automation, l’emplacement canonique pour déclarer les dépendances de modules est au niveau du runbook via `#Requires`, et non au niveau du module d’aide. Le module d’aide expose la dépendance à titre informatif (via `ExternalModuleDependencies` dans le manifeste) et via la vérification d’exécution ci-dessus, afin qu’une mauvaise configuration échoue bruyamment avec un message exploitable plutôt que de masquer silencieusement un conflit de versions.

`Az.Storage` est **pas** requis et ne doit pas être importé dans le même runbook afin d’éviter le conflit d’assembly mentionné ci-dessus.

## Démarrage rapide

L’appel minimal viable nécessite le ou les chemins de fichiers locaux, le nom du conteneur, le groupe de ressources et le nom du compte de stockage :

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

Cela téléverse `devices.csv` dans le conteneur `reports` dans `stcontosoreports` et renvoie un objet avec le nom du blob, l’horodatage d’expiration du SAS et une URL de téléchargement prête à partager, valide pendant les 6 jours par défaut.

## Paramètres

### Obligatoire

| Paramètre            | Type       | Description                                                                                                                                                                                                                                                                                                   |
| -------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FilePaths`          | `string[]` | Un ou plusieurs chemins de fichiers locaux à téléverser. Chaque chemin doit pointer vers un fichier existant (`Test-Path -PathType Leaf`) ; la fonction lève une erreur immédiatement si une entrée est manquante.                                                                                            |
| `ContainerName`      | `chaîne`   | Conteneur de blob cible. Créé automatiquement s’il n’existe pas. Doit respecter les règles de nommage Azure pour les conteneurs (minuscules, 3 à 63 caractères, alphanumériques + tiret). Le nom du conteneur est une *décision par runbook* et est défini dans le runbook, pas dans les paramètres centraux. |
| `ResourceGroupName`  | `chaîne`   | Groupe de ressources qui contient le compte de stockage. Généralement relié au paramètre central `RJReport.StorageAccount.ResourceGroup`.                                                                                                                                                                     |
| `StorageAccountName` | `chaîne`   | Nom du Azure Storage Account. Généralement relié au paramètre central `RJReport.StorageAccount.StorageAccountName`.                                                                                                                                                                                           |

### Optionnel

| Paramètre           | Type     | Par défaut      | Description                                                                                                                                                                                                                                    |
| ------------------- | -------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SubscriptionId`    | `chaîne` | contexte actuel | Abonnement Azure qui héberge le compte de stockage. S’il est fourni, `Set-AzContext -Subscription` est appelé avant toute opération de stockage. À omettre pour utiliser le `Az` contexte.                                                     |
| `LinkExpiryDays`    | `entier` | `6`             | Validité du lien SAS en jours. Validée à `[1, 3650]`. Le même horodatage d’expiration est appliqué à tous les blobs d’un seul appel. Généralement relié au paramètre central `RJReport.StorageAccount.LinkExpiryDays`.                         |
| `AddBlobNamePrefix` | `bool`   | `$false`        | Lorsque `$true`, les noms de blob sont préfixés par `yyyyMMdd-HHmmss-` (horodatage de `Get-Date` au moment du téléversement) afin d’éviter les écrasements lors d’exécutions répétées. Le nom de fichier d’origine est conservé comme suffixe. |

> **Remarque :** Le mappage entre ces paramètres et le JSON de personnalisation central RealmJoin (y compris les valeurs par défaut recommandées) est documenté dans [Paramètres du rapport de runbook — Livraison du Storage Account](/fr/automatisation/runbooks/runbook-report-settings.md#storage-account-delivery).

## Exemples d’utilisation

### Modèle de runbook recommandé

C’est le modèle canonique utilisé par les runbooks de reporting. La configuration de stockage est extraite de la personnalisation centrale RealmJoin via `Use-RJInterface -Type Setting`, le conteneur est codé en dur par runbook, et une configuration manquante provoque l’arrêt du runbook avec un message exploitable :

```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)) {
    "## Pour exporter vers un compte de stockage, veuillez utiliser RJ Runbooks Customization"
    "## ( https://portal.realmjoin.com/settings/runbooks-customizations ) pour configurer :"
    "##   - RJReport.StorageAccount.ResourceGroup"
    "##   - RJReport.StorageAccount.StorageAccountName"
    throw "Configuration du compte de stockage manquante."
}

# … produire le fichier d’export …
$exportPath = "myReport.csv"

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

$uploadResult = $uploadResults[0]
"## Export créé."
"## Expiration du lien : $($uploadResult.EndTime)"
$uploadResult.SASLink | Out-String
```

Quelques conventions utiles à respecter lors de l’adoption de ce modèle :

* Les trois paramètres centraux (`ResourceGroup`, `StorageAccountName`, `LinkExpiryDays`) sont exposés comme paramètres de runbook reliés via `Use-RJInterface -Type Setting`, mais sont généralement *masqués* dans la personnalisation du runbook (`"Hide": true`) afin que les utilisateurs finaux ne les voient jamais.
* Le nom du conteneur est codé en dur par runbook (souvent via une `param` par défaut) afin que les politiques de cycle de vie et les contrôles d’accès puissent être ajustés par type d’export — il ne s’agit volontairement pas *pas* d’un paramètre central.
* `AddBlobNamePrefix $true` est la valeur par défaut sûre pour les exports périodiques qui produisent un nom de fichier fixe à chaque exécution.
* La fonction est appelée à l’intérieur du bloc principal `try { … } catch { throw $_ } finally { Disconnect-AzAccount … }` du runbook afin que les échecs partiels remontent au job Automation et que le contexte Az soit libéré même en cas de succès.

### Plusieurs fichiers dans un seul appel

`FilePaths` accepte un tableau ; chaque fichier est téléversé séquentiellement et un objet résultat est renvoyé pour chaque blob téléversé.

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

foreach ($r in $results) {
    "Téléversé $($r.BlobName) — téléchargeable jusqu’au $($r.EndTime) : $($r.SASLink)"
}
```

### Durée de vie du lien personnalisée et abonnement explicite

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

Utile lorsque le runbook couvre plusieurs abonnements, ou lorsque les destinataires en aval ont besoin d’une fenêtre plus longue que la valeur par défaut de 6 jours.

### À combiner avec `Send-RjRbReportEmail`

Une approche courante consiste à téléverser des données volumineuses dans le stockage blob et à intégrer le lien SAS dans un e-mail de rapport, en gardant l’e-mail bien en dessous de la limite Graph de 4 Mo `sendMail`  :

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

$linkLine = "[Télécharger {0}]({1}) (valide jusqu’au {2:yyyy-MM-dd HH:mm} UTC)" -f `
    $uploaded[0].BlobName, $uploaded[0].SASLink, $uploaded[0].EndTime.ToUniversalTime()

$reportMd = @"
# Inventaire des appareils

La liste complète des appareils est disponible en téléchargement :

$linkLine
"@

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

Voir [Send-RjRbReportEmail](/fr/dev-reference/report-functions/send-rjrbreportemail.md) pour la partie e-mail de ce modèle.

## Comportement et gestion des erreurs

### Validation des fichiers en amont

Avant tout appel Azure, la fonction parcourt `FilePaths` et lève `Le fichier '<path>' est introuvable.` pour la première entrée manquante. Cela évite des téléversements partiels lorsque l’appelant fait une faute de frappe.

### Résolution du contexte Azure

`Get-AzContext` est vérifié en premier. S’il n’y a pas de contexte ou si le contexte n’a pas de `Account` (par ex. une exécution de runbook toute neuve), la fonction appelle `Connect-RjRbAzAccount` pour authentifier l’identité managée. Si `-SubscriptionId` est fourni, `Set-AzContext -Subscription` est appelé ensuite.

### Création du conteneur

Le conteneur est créé avec une requête `PUT …?restype=container`  :

* **HTTP 201** — conteneur créé.
* **HTTP 409** — le conteneur existe déjà ; traité comme un succès.
* **Tout autre statut** — la fonction lève `Échec de la création du conteneur (<status>) : <body>`.

### Échecs de téléversement

Chaque fichier est téléversé via `HttpClient.SendAsync`. Un statut non réussi met fin à l’appel avec `Échec du téléversement du blob (<status>) : <body>`y compris l’erreur brute renvoyée par Azure Storage. Les fichiers précédemment téléversés lors du même appel restent sur le compte de stockage — l’appelant peut vouloir encapsuler l’appel dans un try/catch et effectuer un nettoyage si les téléversements partiels sont inacceptables.

### Échecs de récupération de la clé

`Invoke-AzRestMethod` est utilisé pour appeler l’ARM `listKeys` point de terminaison. Si le statut de réponse est différent de 200, la fonction lève `Échec de la récupération des clés du compte de stockage pour '<account>' dans le groupe de ressources '<rg>'. Statut : <status>`. Les causes les plus courantes sont :

* Absence de `Microsoft.Storage/storageAccounts/listKeys/action` sur l’identité managée.
* Mauvais contexte d’abonnement (à combiner avec `-SubscriptionId`).
* erreur de frappe dans `StorageAccountName` ou `ResourceGroupName`.
* Les paramètres centraux `RJReport.StorageAccount.ResourceGroup` / `RJReport.StorageAccount.StorageAccountName` non configurés — voir [Paramètres du rapport du runbook](/fr/automatisation/runbooks/runbook-report-settings.md#storage-account-delivery).

### Caractéristiques du jeton SAS

Les jetons générés utilisent :

* `sv=2023-11-03` (version signée)
* `sr=b` (limité au blob)
* `sp=r` (lecture seule)
* `spr=https` (HTTPS uniquement)
* `st` défini à 5 minutes dans le passé (tolérance au décalage d’horloge) et `se` à `LinkExpiryDays` à partir du moment de l’appel.

Les jetons sont signés avec la clé du compte de stockage. **Toute personne disposant du lien peut télécharger le blob jusqu’à l’expiration** — traitez l’URL SAS renvoyée comme un secret.

## Sorties

Chaque téléversement réussi produit un `PSCustomObject` avec les propriétés suivantes :

| Propriété      | Type         | Description                                                                                                   |
| -------------- | ------------ | ------------------------------------------------------------------------------------------------------------- |
| `BlobName`     | `chaîne`     | Le nom final du blob dans le conteneur, y compris le préfixe d’horodatage si `AddBlobNamePrefix` est `$true`. |
| `Heure de fin` | `date/heure` | Expiration du SAS en heure locale (également encodée dans l’URL en UTC).                                      |
| `Lien SAS`     | `chaîne`     | URL de téléchargement HTTPS entièrement qualifiée avec SAS Token intégré.                                     |

Les résultats sont renvoyés dans le même ordre que `FilePaths`. Même lors du téléchargement d’un seul fichier, la valeur de retour est un tableau — indexez-le (`$results[0]`) ou itérez avec `foreach` plutôt que de le traiter comme une valeur scalaire.

## Voir aussi

* [Paramètres du rapport de runbook — Livraison du Storage Account](/fr/automatisation/runbooks/runbook-report-settings.md#storage-account-delivery) — configuration centrale du Storage Account, de l’expiration du lien et du préfixe de nom de blob utilisés par les runbooks de reporting.
* [Send-RjRbReportEmail](/fr/dev-reference/report-functions/send-rjrbreportemail.md) — assistant complémentaire pour envoyer les rapports par e-mail ; généralement combiné avec cette fonction pour réduire la taille de la charge utile de l’e-mail.
* Documentation Microsoft : [Autoriser avec une clé partagée](https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key) — schéma de signature utilisé par l’assistant.
* Documentation Microsoft : [Créer un SAS de service](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) — format du SAS Token renvoyé dans `Lien SAS`.


---

# 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/fr/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.
