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

# Publish-RjRbFilesToStorageContainer

## Visão geral

`Publish-RjRbFilesToStorageContainer` é o auxiliar padrão para entregar ficheiros de relatório (CSV, XLSX, ZIP, …) a partir de runbooks de relatórios do RealmJoin via Azure Blob Storage. Carrega um ou mais ficheiros locais para um contentor de destino e devolve uma ligação de transferência SAS com prazo limitado para cada blob, adequada para inclusão em emails de relatório, mensagens do Teams ou saídas do runbook.

Principais características:

* **Não `Az.Storage` dependência** — as operações de blob são realizadas diretamente contra a API REST do Azure Storage (criação de contentor, carregamento, geração de SAS Token). Isto elimina o conhecido conflito de assembly entre `Az.Storage` e `ExchangeOnlineManagement` que surge em runbooks de relatórios mistos.
* **Ligação automática** — se não existir nenhum `Az` contexto ativo, a função chama transparentemente `Connect-RjRbAzAccount`. Uma opção `-SubscriptionId` altera o contexto antes de qualquer operação de armazenamento.
* **Contentor criado automaticamente** — se o contentor de destino ainda não existir, ele é criado automaticamente; um contentor já existente (HTTP 409) é tratado como sucesso.
* **Carregamentos baseados em HttpClient** — usa `System.Net.Http.HttpClient` diretamente porque o `Invoke-RestMethod` interceptor remove os cabeçalhos personalizados obrigatórios (`x-ms-blob-type`) com corpos binários.
* **Ligações SAS só de leitura** — cada URL devolvido é assinado com a chave da Storage Account, limitado a um único blob, apenas HTTPS e válido por `LinkExpiryDays` dias (predefinição 6).

As definições centrais de armazenamento (grupo de recursos, nome da conta, dias de expiração, prefixo do nome do blob) consumidas por um runbook típico residem no JSON de personalização do RealmJoin e estão documentadas em [Definições de relatório do runbook — entrega para Storage Account](/pt/automacao/runbooks/runbook-report-settings.md#storage-account-delivery). Este documento foca-se em chamar a função a partir de um runbook.

## Pré-requisitos

### Azure Storage Account

É necessária uma Azure Storage Account existente (recomenda-se general-purpose v2). O contentor de destino não precisa de existir antecipadamente — é criado automaticamente na primeira utilização.

### Azure RBAC na Storage Account

A identidade gerida da Automation Account (ou o Service Principal usado pelo runbook) precisa das seguintes permissões na Storage Account ou no respetivo grupo de recursos:

| Ação                                                | Necessário para                                                         |
| --------------------------------------------------- | ----------------------------------------------------------------------- |
| `Microsoft.Storage/storageAccounts/read`            | Leitura da Storage Account                                              |
| `Microsoft.Storage/storageAccounts/listKeys/action` | Obter a chave da conta usada para assinatura SharedKey e geração de SAS |

A função incorporada **Storage Account Contributor** cobre ambos. **Storage Blob Data Contributor** por si só é *não* suficiente porque a função assina pedidos com a chave da conta em vez de usar operações de blob suportadas por AAD.

### Conectividade do módulo

A função requer o `Az.Accounts` módulo no ambiente do runbook (`Get-AzContext`, `Set-AzContext`, `Connect-AzAccount`, `Invoke-AzRestMethod`). Declare-o explicitamente no runbook consumidor:

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

Se `Az.Accounts` não estiver disponível em tempo de execução, a função falha rapidamente com uma mensagem de erro clara — verifica `Get-AzContext` logo no início e lança *"Publish-RjRbFilesToStorageContainer requires the 'Az.Accounts' module. Add #Requires -Modules @{ModuleName = 'Az.Accounts'; ModuleVersion = '5.3.4'} to the calling runbook."* antes de qualquer chamada ao Azure ser feita.

> **Porque é que `Az.Accounts` declarado como um `RequiredModules` entrada em `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` está intencionalmente listado apenas em `ExternalModuleDependencies` (informativo) e *não* em `RequiredModules` (imposto em `Import-Module` tempo):
>
> * **Pague apenas pelo que usa.** Muitos runbooks consomem apenas auxiliares baseados em Graph (por exemplo, `Send-RjRbReportEmail` sem `-UseNativeGraphRequest`, ou `Invoke-RjRbRestMethodGraph`) e nunca tocam em nenhum cmdlet Az.\*. Promover `Az.Accounts` para `RequiredModules` obrigaria todos os runbooks consumidores a incluir o módulo mesmo quando nada no seu caminho de código dele precisa — aumentando de forma mensurável o tempo de arranque a frio no Azure Automation.
> * **Evite conflitos de versões.** Uma `RequiredModules` restrição rígida ativa a resolução automática no momento da importação e pode puxar uma `Az.Accounts` versão específica que entra em conflito com o que o próprio runbook fixa (os submódulos Az.\* são notoriamente sensíveis a versões). Permitir que o runbook declare o seu próprio `#Requires -Modules` mantém a escolha da versão com o chamador.
> * **Autoridade por runbook.** No Azure Automation, o local canónico para declarar requisitos de módulos é ao nível do runbook através de `#Requires`, não ao nível do módulo auxiliar. O módulo auxiliar expõe a dependência de forma informativa (via `ExternalModuleDependencies` no manifesto) e através da verificação em tempo de execução acima, para que uma má configuração falhe de forma ruidosa com uma mensagem acionável em vez de mascarar silenciosamente um conflito de versões.

`Az.Storage` é **não** é necessário e não deve ser importado no mesmo runbook para evitar o conflito de assembly mencionado acima.

## Início rápido

A chamada mínima viável requer o(s) caminho(s) local(is) do ficheiro, o nome do contentor, o nome do grupo de recursos e o nome da 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
```

Isto carrega `devices.csv` para o `reports` contentor em `stcontosoreports` e devolve um objeto com o nome do blob, o carimbo de data/hora de expiração do SAS e um URL de transferência pronto a partilhar válido por predefinição durante 6 dias.

## Parâmetros

### Obrigatório

| Parâmetro            | Tipo       | Descrição                                                                                                                                                                                                                                                                                   |
| -------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FilePaths`          | `string[]` | Um ou mais caminhos locais de ficheiro para carregar. Cada caminho deve apontar para um ficheiro existente (`Test-Path -PathType Leaf`); a função lança uma exceção logo no início se alguma entrada estiver em falta.                                                                      |
| `ContainerName`      | `string`   | Contentor blob de destino. Criado automaticamente se não existir. Deve cumprir as regras de nomenclatura de contentores do Azure (minúsculas, 3–63 caracteres, alfanumérico + hífen). O nome do contentor é uma *por runbook* decisão e é definido no runbook, não nas definições centrais. |
| `ResourceGroupName`  | `string`   | Grupo de recursos que contém a Storage Account. Normalmente ligado à definição central `RJReport.AzureStorage.ResourceGroup`.                                                                                                                                                               |
| `StorageAccountName` | `string`   | Nome da Azure Storage Account. Normalmente ligado à definição central `RJReport.AzureStorage.StorageAccountName`.                                                                                                                                                                           |

### Opcional

| Parâmetro           | Tipo     | Predefinição   | Descrição                                                                                                                                                                                                                                     |
| ------------------- | -------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SubscriptionId`    | `string` | contexto atual | Subscrição Azure que aloja a Storage Account. Se for fornecida, `Set-AzContext -Subscription` é chamada antes de qualquer operação de armazenamento. Omitir para usar o `Az` contexto.                                                        |
| `LinkExpiryDays`    | `int`    | `6`            | Validade da ligação SAS em dias. Validado para `[1, 3650]`. O mesmo carimbo de data/hora de expiração é aplicado a todos os blobs numa única chamada. Normalmente ligado à definição central `RJReport.AzureStorage.LinkExpiryDays`.          |
| `AddBlobNamePrefix` | `bool`   | `$false`       | Quando `$true`, os nomes dos blobs são prefixados com `yyyyMMdd-HHmmss-` (carimbo de data/hora de `Get-Date` no momento do carregamento) para evitar substituições em execuções repetidas. O nome original do ficheiro é mantido como sufixo. |

> **Nota:** O mapeamento entre estes parâmetros e o JSON central de personalização do RealmJoin (incluindo os valores predefinidos recomendados) está documentado em [Definições de relatório do runbook — entrega para Storage Account](/pt/automacao/runbooks/runbook-report-settings.md#storage-account-delivery).

## Exemplos de utilização

### Padrão recomendado de runbook

Este é o padrão canónico usado pelos runbooks de relatórios. A configuração de armazenamento é obtida da personalização central do RealmJoin através de `Use-RJInterface -Type Setting`, o contentor é codificado por runbook e uma configuração em falta faz com que o runbook aborte com uma mensagem acionável:

```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)) {
    "## Para exportar para uma Storage Account, utilize a RJ Runbooks Customization"
    "## ( https://portal.realmjoin.com/settings/runbooks-customizations ) para configurar:"
    "##   - RJReport.AzureStorage.ResourceGroup"
    "##   - RJReport.AzureStorage.StorageAccountName"
    throw "Falta configuração da Storage Account."
}

# … gerar o ficheiro de exportação …
$exportPath = "myReport.csv"

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

$uploadResult = $uploadResults[0]
"## Exportação criada."
"## Expiração da ligação: $($uploadResult.EndTime)"
$uploadResult.SASLink | Out-String
```

Há algumas convenções que vale a pena manter ao adotar este padrão:

* As três definições centrais (`ResourceGroup`, `StorageAccountName`, `LinkExpiryDays`) são expostas como parâmetros do runbook ligados através de `Use-RJInterface -Type Setting`, mas normalmente *ocultos* na personalização do runbook (`"Hide": true`) para que os utilizadores finais nunca as vejam.
* O nome do contentor é codificado por runbook (muitas vezes através de um `param` predefinido) para que as políticas de ciclo de vida e os controlos de acesso possam ser ajustados por tipo de exportação — isso é intencionalmente *não* uma definição central.
* `AddBlobNamePrefix $true` é a predefinição segura para exportações periódicas que produzem um nome de ficheiro fixo em cada execução.
* A função é chamada dentro do principal `try { … } catch { throw $_ } finally { Disconnect-AzAccount … }` bloco, para que falhas parciais subam para o trabalho do Automation e o contexto Az seja libertado mesmo em caso de sucesso.

### Vários ficheiros numa única chamada

`FilePaths` aceita um array; cada ficheiro é carregado sequencialmente e é devolvido um objeto de resultado para cada blob carregado.

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

foreach ($r in $results) {
    "Carregado $($r.BlobName) — transferência até $($r.EndTime): $($r.SASLink)"
}
```

### Duração personalizada da ligação e subscrição explícita

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

Útil quando o runbook abrange várias subscrições, ou quando os destinatários a jusante precisam de uma janela maior do que a predefinição de 6 dias.

### Combinando com `Send-RjRbReportEmail`

Um padrão comum é carregar dados volumosos para blob storage e incorporar a ligação SAS num email de relatório, mantendo o email bem abaixo do limite de 4 MB do Graph `sendMail` limite:

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

$linkLine = "[Download {0}]({1}) (válido até {2:yyyy-MM-dd HH:mm} UTC)" -f `
    $uploaded[0].BlobName, $uploaded[0].SASLink, $uploaded[0].EndTime.ToUniversalTime()

$reportMd = @"
# Inventário de dispositivos

A lista completa de dispositivos está disponível para transferência:

$linkLine
"@

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

Ver [Send-RjRbReportEmail](/pt/dev-reference/report-functions/send-rjrbreportemail.md) para o lado do email deste padrão.

## Comportamento e tratamento de erros

### Validação prévia de ficheiros

Antes de qualquer chamada ao Azure ser feita, a função itera sobre `FilePaths` e lança `O ficheiro '<path>' não foi encontrado.` para a primeira entrada em falta. Isto evita carregamentos parciais quando o chamador introduz um erro de escrita.

### Resolução do contexto Azure

`Get-AzContext` é verificado primeiro. Se não houver contexto ou o contexto não tiver `Account` (por exemplo, uma execução nova do runbook), a função chama `Connect-RjRbAzAccount` para autenticar a identidade gerida. Se `-SubscriptionId` for fornecida, `Set-AzContext -Subscription` é invocado a seguir.

### Criação do contentor

O contentor é criado com um pedido `PUT …?restype=container` de:

* **HTTP 201** — contentor criado.
* **HTTP 409** — o contentor já existe; tratado como sucesso.
* **Qualquer outro estado** — a função lança `Falha na criação do contentor (<status>): <body>`.

### Falhas no carregamento

Cada ficheiro é carregado via `HttpClient.SendAsync`. Um estado de não sucesso termina a chamada com `Falha no carregamento do blob (<status>): <body>`, incluindo o erro bruto devolvido pelo Azure Storage. Os ficheiros anteriores que já tenham sido carregados na mesma chamada permanecem na Storage Account — o chamador pode querer envolver a chamada num try/catch e executar limpeza se carregamentos parciais forem inaceitáveis.

### Falhas na obtenção da chave

`Invoke-AzRestMethod` é usado para chamar o ARM `listKeys` endpoint. Se o estado da resposta for diferente de 200, a função lança `Falha ao obter as chaves da storage account para '<account>' no grupo de recursos '<rg>'. Estado: <status>`. As causas mais comuns são:

* Falta `Microsoft.Storage/storageAccounts/listKeys/action` na identidade gerida.
* Contexto de subscrição errado (combine com `-SubscriptionId`).
* Erro de escrita em `StorageAccountName` ou `ResourceGroupName`.
* As definições centrais `RJReport.AzureStorage.ResourceGroup` / `RJReport.AzureStorage.StorageAccountName` não estão configuradas — ver [Definições do Relatório do Runbook](/pt/automacao/runbooks/runbook-report-settings.md#storage-account-delivery).

### Características do SAS Token

Os tokens gerados usam:

* `sv=2023-11-03` (versão assinada)
* `sr=b` (delimitado a blob)
* `sp=r` (só leitura)
* `spr=https` (apenas HTTPS)
* `st` definido para 5 minutos no passado (tolerância ao desvio do relógio) e `se` para `LinkExpiryDays` a partir do momento da chamada.

Os tokens são assinados com a chave da Storage Account. **Qualquer pessoa com a ligação pode transferir o blob até à expiração** — trate o URL SAS devolvido como um segredo.

## Saídas

Cada carregamento bem-sucedido produz um `PSCustomObject` com estas propriedades:

| Propriedade | Tipo       | Descrição                                                                                                        |
| ----------- | ---------- | ---------------------------------------------------------------------------------------------------------------- |
| `BlobName`  | `string`   | O nome final do blob no contentor, incluindo o prefixo de carimbo de data/hora se `AddBlobNamePrefix` é `$true`. |
| `EndTime`   | `datetime` | Expiração SAS em hora local (também codificada no URL como UTC).                                                 |
| `SASLink`   | `string`   | URL de transferência HTTPS totalmente qualificado com SAS Token incorporado.                                     |

Os resultados são devolvidos na mesma ordem que `FilePaths`. Mesmo ao carregar um único ficheiro, o valor de retorno é um array — aceda-lhe por índice (`$results[0]`) ou itere com `foreach` em vez de o tratar como um escalar.

## Ver também

* [Definições de relatório do runbook — entrega para Storage Account](/pt/automacao/runbooks/runbook-report-settings.md#storage-account-delivery) — configuração central da Storage Account, da expiração da ligação e do prefixo do nome do blob usada pelos runbooks de relatórios.
* [Send-RjRbReportEmail](/pt/dev-reference/report-functions/send-rjrbreportemail.md) — auxiliar complementar para entregar relatórios por email; normalmente combinado com esta função para manter a carga útil do email pequena.
* Microsoft Docs: [Autorizar com Shared Key](https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key) — esquema de assinatura usado pelo auxiliar.
* Microsoft Docs: [Criar um service SAS](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) — formato do SAS Token devolvido em `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/pt/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.
