> 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 dos runbooks de relatório do RealmJoin via Azure Blob Storage. Carrega um ou mais ficheiros locais para um contentor de destino e devolve uma ligação SAS de download com validade limitada para cada blob, adequada para inclusão em e-mails de relatório, mensagens no Teams ou saídas do runbook.

Características principais:

* **Sem `Az.Storage` dependência** — as operações de blob são realizadas diretamente contra a API REST de Azure Storage (criação de contentor, carregamento, geração de token SAS). Isto elimina o conhecido conflito de assembly entre `Az.Storage` e `ExchangeOnlineManagement` que surge em runbooks de relatório mistos.
* **Ligação automática** — se não existir nenhum `Az` contexto ativo, a função chama transparentemente `Connect-RjRbAzAccount`. Um `-SubscriptionId` altera o contexto antes de qualquer operação de armazenamento.
* **Contentor criado automaticamente** — se o contentor de destino ainda não existir, é criado automaticamente; um contentor existente (HTTP 409) é tratado como sucesso.
* **Carregamentos baseados em HttpClient** — usa `System.Net.Http.HttpClient` diretamente porque o interceptor do Azure Automation `Invoke-RestMethod` remove os cabeçalhos personalizados obrigatórios (`x-ms-blob-type`) em 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) usadas 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 da Storage Account](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery). Este documento centra-se na chamada da 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 previamente — é 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` | Obtenção da chave da conta usada para assinatura SharedKey e geração de SAS |

A função incorporada **Storage Account Contributor** abrange ambas. **Storage Blob Data Contributor** por si só não é *suficiente* porque a função assina os pedidos com a chave da conta em vez de utilizar 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 antecipadamente a existência de `Get-AzContext` e lança *"Publish-RjRbFilesToStorageContainer requer o módulo 'Az.Accounts'. Adicione #Requires -Modules @{ModuleName = 'Az.Accounts'; ModuleVersion = '5.3.4'} ao runbook chamador."* antes de qualquer chamada a Azure ser feita.

> **Porque não está `Az.Accounts` declarado como um `RequiredModules` na entrada `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` está intencionalmente listado apenas em `ExternalModuleDependencies` (informativo) e *suficiente* em `RequiredModules` (aplicado em `Import-Module` tempo):
>
> * **Paga apenas pelo que usas.** Muitos runbooks consomem apenas auxiliares baseados em Graph (por ex., `Send-RjRbReportEmail` sem `-UseNativeGraphRequest`, ou `Invoke-RjRbRestMethodGraph`) e nunca usam qualquer cmdlet Az.\*. Promover `Az.Accounts` para `RequiredModules` forçaria cada runbook consumidor a incluir o módulo mesmo quando nada no respetivo caminho de código o exigisse — aumentando de forma mensurável o tempo de arranque a frio no Azure Automation.
> * **Evita conflitos de versões.** Uma `RequiredModules` restrição rígida desencadeia resolução automática no momento da importação e pode trazer uma `Az.Accounts` versão que entra em conflito com aquilo que o próprio runbook fixa (os submódulos Az.\* são notoriamente sensíveis à versão). Deixar o runbook declarar 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 via `#Requires`, e 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 a má configuração falhe de forma explícita com uma mensagem acionável em vez de mascarar silenciosamente um conflito de versões.

`Az.Storage` é **suficiente** 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 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 download pronto a partilhar válido por 6 dias, por defeito.

## Parâmetros

### Obrigatório

| Parâmetro            | Tipo       | Descrição                                                                                                                                                                                                                                                                                 |
| -------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `FilePaths`          | `string[]` | Um ou mais caminhos de ficheiros locais para carregar. Cada caminho tem de apontar para um ficheiro existente (`Test-Path -PathType Leaf`); a função lança antecipadamente se alguma entrada estiver em falta.                                                                            |
| `ContainerName`      | `string`   | Contentor de blobs de destino. Criado automaticamente se não existir. Tem de 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* 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.StorageAccount.ResourceGroup`.                                                                                                                                                           |
| `StorageAccountName` | `string`   | Nome da Azure Storage Account. Normalmente ligado à definição central `RJReport.StorageAccount.StorageAccountName`.                                                                                                                                                                       |

### Opcional

| Parâmetro           | Tipo     | Padrão         | Descrição                                                                                                                                                                                                                                     |
| ------------------- | -------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SubscriptionId`    | `string` | contexto atual | subscrição Azure que aloja a storage account. Se for fornecido, `Set-AzContext -Subscription` é chamado antes de qualquer operação de armazenamento. Omita para usar o `Az` contexto.                                                         |
| `LinkExpiryDays`    | `int`    | `6`            | Validade da ligação SAS em dias. Validado para `[1, 3650]`. `RJReport.StorageAccount.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 de personalização central do RealmJoin (incluindo os valores predefinidos recomendados) está documentado em [Definições de Relatório do Runbook — Entrega da Storage Account](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery).

## Exemplos de utilização

### Padrão de runbook recomendado

Este é o padrão canónico usado pelos runbooks de relatório. A configuração de armazenamento é obtida da personalização central do RealmJoin via `Use-RJInterface -Type Setting`, o contentor é codificado por runbook e uma configuração em falta faz com que o runbook termine 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.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)) {
    "## Para exportar para uma Storage Account, utilize a Personalização de Runbooks do RJ"
    "## ( https://portal.realmjoin.com/settings/runbooks-customizations ) para configurar:"
    "##   - RJReport.StorageAccount.ResourceGroup"
    "##   - RJReport.StorageAccount.StorageAccountName"
    throw "Falta a configuração da Storage Account."
}

# … produzir 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`) `Use-RJInterface -Type Setting`, mas normalmente *ocultos* na personalização do runbook (`"Hide": true`) para que os utilizadores finais nunca os vejam.
* O nome do contentor é codificado por runbook (frequentemente 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 — é intencionalmente *suficiente* 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 as falhas parciais cheguem ao trabalho de 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) — descarregar até $($r.EndTime): $($r.SASLink)"
}
```

### Vida útil 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 finais precisam de uma janela maior do que a predefinição de 6 dias.

### Combinação com `Send-RjRbReportEmail`

Um padrão comum é carregar dados volumosos para armazenamento de blobs e incorporar a ligação SAS num e-mail de relatório, mantendo o e-mail 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()

@"
# Inventário de Dispositivos

A lista completa de dispositivos está disponível para download:

$linkLine
"@

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

Consulte [Send-RjRbReportEmail](/pt/dev-reference/report-functions/send-rjrbreportemail.md) para a parte do e-mail deste padrão.

## Comportamento e tratamento de erros

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

Antes de ser feita qualquer chamada a Azure, a função percorre `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 digitação.

### Resolução do contexto Azure

`Get-AzContext` é verificado primeiro. Se não houver contexto ou se 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 fornecido, `Set-AzContext -Subscription` é invocado de seguida.

### Criação do contentor

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

* **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 de carregamento

Cada ficheiro é carregado via `HttpClient.SendAsync`. Um estado não bem-sucedido termina a chamada com `Falha no carregamento do blob (<status>): <body>`, incluindo o erro bruto devolvido por 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:

* Ausência de `Microsoft.Storage/storageAccounts/listKeys/action` na identidade gerida.
* Contexto de subscrição incorreto (combine com `-SubscriptionId`).
* Erro de digitação em `StorageAccountName` ou `ResourceGroupName`.
* As definições centrais `RJReport.StorageAccount.ResourceGroup` / `RJReport.StorageAccount.StorageAccountName` não configuradas — consulte [Definições de relatórios do runbook](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery).

### Características do token SAS

Os tokens gerados usam:

* `sv=2023-11-03` (versão assinada)
* `sr=b` (limitado a blob)
* `sp=r` (só de leitura)
* `spr=https` (apenas HTTPS)
* `st` definido para 5 minutos no passado (tolerância a 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 descarregar 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 data/hora se `AddBlobNamePrefix` é `$true`. |
| `Hora de término` | `data e hora` | Expiração do SAS no horário local (também codificada no URL como UTC).                                |
| `SASLink`         | `string`      | URL de download HTTPS totalmente qualificada com SAS Token incorporado.                               |

Os resultados são retornados na mesma ordem que `FilePaths`. Mesmo ao carregar um único ficheiro, o valor de retorno é um array — indexe-o (`$results[0]`) ou itere com `foreach` em vez de tratá-lo como um escalar.

## Ver também

* [Definições de Relatório do Runbook — Entrega da Storage Account](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery) — configuração central de Storage Account, expiração do link e prefixo do nome do blob usados pelos runbooks de relatórios.
* [Send-RjRbReportEmail](/pt/dev-reference/report-functions/send-rjrbreportemail.md) — função auxiliar complementar para entregar relatórios por e-mail; normalmente combinada com esta função para manter pequeno o payload do e-mail.
* Microsoft Docs: [Autorizar com Shared Key](https://learn.microsoft.com/en-us/rest/api/storageservices/authorize-with-shared-key) — esquema de assinatura usado pela função auxiliar.
* Microsoft Docs: [Criar um SAS de serviço](https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas) — formato do SAS Token retornado 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.
