> 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

Carregar ficheiros locais para um contentor Azure Storage a partir de runbooks do Azure Automation e devolver ligações de transferência SAS com tempo limitado.

## Visão geral

`Publish-RjRbFilesToStorageContainer` é o auxiliar padrão para entregar arquivos de relatório (CSV, XLSX, ZIP, …) a partir de runbooks de relatório do RealmJoin via Azure Blob Storage. Ele carrega um ou mais arquivos locais para um contêiner de destino e retorna um link de download SAS com tempo limitado para cada blob, adequado para inclusão em e-mails de relatório, mensagens do Teams ou saídas de runbook.

Características principais:

* **Sem `Az.Storage` dependência** — as operações de blob são executadas diretamente contra a API REST do Azure Storage (criação de contêiner, upload, geração de SAS Token). Isso elimina o conhecido conflito de assembly entre `Az.Storage` e `ExchangeOnlineManagement` que aparece em runbooks de relatório mistos.
* **Autoconexão** — se nenhum `Az` contexto estiver ativo, a função chama transparentemente `Connect-RjRbAzAccount`. Um `-SubscriptionId` opcional alterna o contexto antes de qualquer operação de storage.
* **Contêiner criado automaticamente** — se o contêiner de destino ainda não existir, ele é criado automaticamente; um contêiner existente (HTTP 409) é tratado como sucesso.
* **Uploads baseados em HttpClient** — usa `System.Net.Http.HttpClient` diretamente porque o `Invoke-RestMethod` do Azure Automation remove cabeçalhos personalizados obrigatórios (`x-ms-blob-type`) com corpos binários.
* **Links SAS somente leitura** — cada URL retornada é assinada com a chave da Storage Account, limitada a um único blob, somente HTTPS e válida por `LinkExpiryDays` dias (padrão 6).

As configurações centrais de storage (grupo de recursos, nome da conta, dias de expiração, prefixo do nome do blob) consumidas por um runbook típico vivem no JSON de personalização do RealmJoin e estão documentadas em [Runbook Report Settings — Storage Account Delivery](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery). Este documento se concentra em chamar a função a partir de um runbook.

## Pré-requisitos

### Azure Storage Account

É necessário uma Azure Storage Account existente (recomendado geral v2). O contêiner de destino não precisa existir previamente — ele é criado automaticamente no primeiro uso.

### Azure RBAC na storage account

A identidade gerenciada da Automation Account (ou o service principal usado pelo runbook) precisa das seguintes permissões na storage account:

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

Atribua a função interna **Reader and Data Access** na storage account: ela é a menor função interna que cobre ambos. A criação de contêiner, o upload e a geração de SAS são autorizados com a chave da conta e não precisam de permissões adicionais de Azure RBAC. **Storage Account Contributor** também funciona, mas além disso permite gerenciar e excluir a storage account. **Storage Blob Data Contributor** sozinho *não* é suficiente, porque a função assina as solicitações com a chave da conta em vez de usar operações de blob baseadas em AAD.

Como a função se autentica com a chave da conta, o acesso por chave compartilhada deve estar permitido na storage account (`allowSharedKeyAccess` não deve ser `false`). A chave concede acesso a todos os dados na storage account, portanto é recomendada uma storage account dedicada para relatórios.

### Conectividade do módulo

A função requer o módulo `Az.Accounts` 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 — ela verifica `Get-AzContext` antecipadamente 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 que qualquer chamada Azure seja feita.

> **Por que não `Az.Accounts` está declarado como uma entrada `RequiredModules` em `RealmJoin.RunbookHelper.psd1`?**
>
> `Az.Accounts` é listado intencionalmente apenas em `ExternalModuleDependencies` (informativo) e *não* em `RequiredModules` (aplicado em `Import-Module` tempo):
>
> * **Pague apenas pelo que você usa.** Muitos runbooks consomem apenas auxiliares baseados em Graph (por exemplo, `Send-RjRbReportEmail` sem `-UseNativeGraphRequest`, ou `Invoke-RjRbRestMethodGraph`) e nunca usam nenhum cmdlet Az.\*. Promover `Az.Accounts` para `RequiredModules` obrigaria todo runbook consumidor a enviar o módulo mesmo quando nada no caminho de código dele precisasse disso — aumentando de forma mensurável o tempo de inicialização a frio no Azure Automation.
> * **Evite disputas de versão.** Uma `RequiredModules` restrição rígida aciona a resolução automática no momento da importação e pode trazer uma `Az.Accounts` versão específica que conflita com o que o próprio runbook fixa (submódulos Az.\* são notoriamente sensíveis a versão). Deixar o runbook declarar seu próprio `#Requires -Modules` mantém a escolha de versão com o chamador.
> * **Autoridade por runbook.** No Azure Automation, o local canônico para declarar requisitos de módulo é no nível do runbook via `#Requires`, não no nível do módulo auxiliar. O módulo auxiliar expõe a dependência de forma informativa (via `ExternalModuleDependencies` no manifesto) e por meio da verificação em tempo de execução acima, de modo que uma configuração incorreta falha de forma explícita com uma mensagem acionável em vez de mascarar silenciosamente um conflito de versão.

`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) do arquivo local, o nome do contêiner, 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
```

Isso faz upload de `devices.csv` para o contêiner `reports` em `stcontosoreports` e retorna um objeto com o nome do blob, o carimbo de data/hora de expiração do SAS e uma URL de download pronta para compartilhamento, válida pelos 6 dias padrão.

## Parâmetros

### Obrigatório

| Parâmetro            | Tipo       | Descrição                                                                                                                                                                                                                                                                                        |
| -------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `FilePaths`          | `string[]` | Um ou mais caminhos de arquivos locais para upload. Cada caminho deve apontar para um arquivo existente (`Test-Path -PathType Leaf`); a função lança imediatamente se qualquer entrada estiver ausente.                                                                                          |
| `ContainerName`      | `string`   | Contêiner de blob de destino. Criado automaticamente se não existir. Deve obedecer às regras de nomenclatura de contêiner do Azure (minúsculas, 3–63 caracteres, alfanumérico + hífen). O nome do contêiner é uma decisão *por runbook* e é definido no runbook, não nas configurações centrais. |
| `ResourceGroupName`  | `string`   | Grupo de recursos que contém a storage account. Normalmente vinculado à configuração central `RJReport.StorageAccount.ResourceGroup`.                                                                                                                                                            |
| `StorageAccountName` | `string`   | Nome da Azure Storage Account. Normalmente vinculado à configuração central `RJReport.StorageAccount.StorageAccountName`.                                                                                                                                                                        |

### Opcional

| Parâmetro           | Tipo     | Padrão         | Descrição                                                                                                                                                                                                                             |
| ------------------- | -------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SubscriptionId`    | `string` | contexto atual | Assinatura Azure que hospeda a storage account. Se fornecida, `Set-AzContext -Subscription` é chamado antes de qualquer operação de storage. Omitir para usar o `Az` contexto.                                                        |
| `LinkExpiryDays`    | `int`    | `6`            | Validade do link SAS em dias. Validado para `[1, 3650]`. O mesmo carimbo de expiração é aplicado a todos os blobs em uma única chamada. Normalmente vinculado à configuração central `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 upload) para evitar sobrescritas em execuções repetidas. O nome original do arquivo é mantido como sufixo. |

> **Observação:** O mapeamento entre esses parâmetros e o JSON de personalização central do RealmJoin (incluindo os padrões recomendados) está documentado em [Runbook Report Settings — Storage Account Delivery](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery).

## Exemplos de uso

### Padrão de runbook recomendado

Este é o padrão canônico usado por runbooks de relatório. A configuração de storage é obtida da personalização central do RealmJoin via `Use-RJInterface -Type Setting`, o contêiner é codificado por runbook e uma configuração ausente faz o runbook abortar 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, use a Customização de Runbooks do RJ"
    "## ( https://portal.realmjoin.com/settings/runbooks-customizations ) para configurar:"
    "##   - RJReport.StorageAccount.ResourceGroup"
    "##   - RJReport.StorageAccount.StorageAccountName"
    throw "Missing Storage Account Configuration."
}

# … produza o arquivo 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 do link: $($uploadResult.EndTime)"
$uploadResult.SASLink | Out-String
```

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

* As três configurações centrais (`ResourceGroup`, `StorageAccountName`, `LinkExpiryDays`) são expostas como parâmetros do runbook vinculados por meio de `Use-RJInterface -Type Setting`, mas normalmente *ocultos* na personalização do runbook (`"Hide": true`) para que os usuários finais nunca os vejam.
* O nome do contêiner é codificado por runbook (muitas vezes por meio de um valor padrão de `param` ) para que políticas de ciclo de vida e controles de acesso possam ser ajustados por tipo de exportação — ele é intencionalmente *não* uma configuração central.
* `AddBlobNamePrefix $true` é o padrão seguro para exportações periódicas que produzem um nome de arquivo fixo em cada execução.
* A função é chamada dentro do principal `try { … } catch { throw $_ } finally { Disconnect-AzAccount … }` bloco, para que falhas parciais cheguem ao job do Automation e o contexto Az seja liberado mesmo em caso de sucesso.

### Vários arquivos em uma chamada

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

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

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

### Tempo de vida personalizado do link e assinatura 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 assinaturas, ou quando os destinatários downstream precisam de uma janela maior do que o padrão de 6 dias.

### Combinando com `Send-RjRbReportEmail`

Um padrão comum é enviar dados volumosos para blob storage e incorporar o link SAS em um 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()

$reportMd = @"
# 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
```

Veja [Send-RjRbReportEmail](/pt/dev-reference/report-functions/send-rjrbreportemail.md) para o lado de e-mail deste padrão.

## Comportamento e tratamento de erros

### Validação de arquivo antecipada

Antes que qualquer chamada Azure seja feita, a função percorre `FilePaths` e lança `File '<path>' was not found.` para a primeira entrada ausente. Isso evita uploads parciais quando o chamador passa 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 de runbook recém-criada), a função chama `Connect-RjRbAzAccount` para autenticar a identidade gerenciada. Se `-SubscriptionId` for fornecido, `Set-AzContext -Subscription` é invocado em seguida.

### Criação do contêiner

O contêiner é criado com uma solicitação `PUT …?restype=container` :

* **HTTP 201** — contêiner criado.
* **HTTP 409** — o contêiner já existe; tratado como sucesso.
* **Qualquer outro status** — a função lança `Falha ao criar contêiner (<status>): <body>`. Um HTTP 403 com `KeyBasedAuthenticationNotPermitted` significa que o acesso por chave compartilhada está desabilitado na storage account.

### Falhas de upload

Cada arquivo é carregado via `HttpClient.SendAsync`. Um status diferente de sucesso encerra a chamada com `Falha no upload do blob (<status>): <body>`, incluindo o erro bruto retornado pelo Azure Storage. Arquivos anteriores que já tenham sido carregados na mesma chamada permanecem na storage account — o chamador pode querer envolver a chamada em um try/catch e executar limpeza se uploads parciais forem inaceitáveis.

### Falhas na recuperação da chave

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

* Ausência de `Microsoft.Storage/storageAccounts/listKeys/action` na identidade gerenciada.
* Contexto de assinatura incorreto (combine com `-SubscriptionId`).
* Erro de digitação em `StorageAccountName` ou `ResourceGroupName`.
* As definições centrais `RJReport.StorageAccount.ResourceGroup` / `RJReport.StorageAccount.StorageAccountName` não configurado — veja [Configurações do Relatório do Runbook](/pt/automatizacao/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` (no âmbito do blob)
* `sp=r` (somente leitura)
* `spr=https` (apenas HTTPS)
* `st` definido para 5 minutos no passado (tolerância a diferença de 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 o link pode baixar o blob até à expiração** — trate o URL SAS devolvido como um segredo.

## Saídas

Cada upload 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 local do SAS (também codificada no URL como UTC).                                                       |
| `SASLink`   | `string`   | URL de download HTTPS totalmente qualificado com SAS Token incorporado.                                           |

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

## Ver também

* [Runbook Report Settings — Storage Account Delivery](/pt/automatizacao/runbooks/runbook-report-settings.md#storage-account-delivery) — configuração central da Storage Account, da expiração do link e do prefixo do nome do blob usados pelos runbooks de relatório.
* [Send-RjRbReportEmail](/pt/dev-reference/report-functions/send-rjrbreportemail.md) — auxiliar complementar para enviar relatórios por email; normalmente combinado com esta função para manter pequena a carga útil do email.
* 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 SAS de serviço](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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

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

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
