# Personalização de Runbooks

## Overview

A implementação do runbook do RealmJoin oferece capacidades de personalização ao autor de um runbook ou ao administrador de um ambiente, para que possam:

* Hospedar parâmetros e modelos específicos do cliente/Tenant
* Oferecer elementos de UI como seletores de utilizadores ou listas suspensas
* Apresentar explicações legíveis por humanos dos parâmetros
* Ocultar elementos de UI desnecessários

<figure><img src="/files/fc10f116696800198ba3d0f0bf0012c61f38abbc" alt=""><figcaption></figcaption></figure>

As personalizações podem ser incluídas no próprio runbook e/ou armazenadas na instância do RealmJoin Portal do cliente. Por padrão, tentaremos oferecer padrões sensatos nos runbooks oferecidos em [GitHub](https://github.com/realmjoin/realmjoin-runbooks).

Alguns runbooks virão com exemplos de como configurar modelos específicos do cliente, como especificar localizações de Office para o onboarding do utilizador.

### Formatar

A personalização pode ser definida (em ordem decrescente de prioridade)

* Bloco de JSON em [Definições do RealmJoin Portal](https://portal.realmjoin.com/settings/runbooks-customizations), substituindo o comportamento padrão do runbook
* Bloco de JSON no cabeçalho de um runbook

Adicionalmente (com menor prioridade)

* por parâmetro no cabeçalho do runbook
* por parâmetro no bloco de parâmetros dos runbooks (usando o módulo auxiliar RJRb)

Algumas funcionalidades (como modelos) só estão disponíveis em formato JSON. Algumas funcionalidades (como criar um seletor de utilizador) só estão disponíveis especificando um tipo de dados no bloco de parâmetros. Você pode combinar vários tipos de personalizações para obter melhores resultados.

## Bloco de Parâmetros do Runbook

O RealmJoin Portal analisa o bloco de parâmetros PowerShell de um runbook para determinar quais campos de entrada renderizar. Sempre que possível, também validará as entradas de acordo com o tipo .NET fornecido para uma variável.

Os seguintes tipos de dados são atualmente compreendidos:

* `[bool]`, `[boolean]` - apresentará um alternador binário
* `[string]` - apresentará uma caixa de texto para digitar qualquer entrada alfanumérica
* `[int]` - apresentará uma caixa de texto, permitindo apenas entradas numéricas
* `[DateTime]`, `[DateTimeOffset]` - apresentará um seletor de data/hora

Você pode aplicar modificadores padrão do PowerShell aos parâmetros. O RealmJoin Portal, em particular, entenderá se você especificar `[Parameter(Mandatory = $true)]` para indicar um parâmetro obrigatório e impor que esses parâmetros sejam preenchidos.

Sempre que possível, o RealmJoin Portal também lerá e apresentará os valores padrão fornecidos na UI.

Tenha em atenção que os valores padrão do runbook podem ser substituídos por personalizações. Além disso, os parâmetros podem ser completamente ocultados por personalizações.

### Personalização de Parâmetros

Para poder personalizar parâmetros, certifique-se de incluir o módulo PS RealmJoin Runbook Helper no seu runbook:

`#Requires -Modules @{ModuleName = "RealmJoin.RunbookHelper"; ModuleVersion = "0.6.0" }`

Pode então incluir `[ValidateScript( { Use-RJInterface ... } )]` declarações nas definições de parâmetros. Por exemplo, o seguinte criará um seletor de utilizador, permitindo escolher um utilizador do Entra ID e passará o respetivo ID de objeto como string para o runbook.

```powershell
param(
    [ValidateScript( { Use-RJInterface -DisplayName "Atribuir dispositivo a este utilizador (opcional)" -Type Graph -Entity User } )]
    [string] $AssignedUserId = ""
)
```

Vamos analisar isto passo a passo. `[ValidateScript...]` é um modificador para o próximo parâmetro definido no bloco param. Neste caso, a variável `$AssignedUserId`.

`Use-RJInterface` faz parte do nosso [RealmJoin Runbook Helper](https://github.com/realmjoin/RealmJoin.RunbookHelper) módulo PowerShell. Permite especificar que tipo de entrada espera usando `-Type` e `-Entity`, se isso ainda não estiver totalmente definido pelo tipo de variável.

`-DisplayName` permite passar ao RealmJoin Portal um pedido / descrição legível por humanos para este parâmetro.

#### Recursos do Graph

No exemplo acima, a origem da informação é o MS Graph, conforme descrito por `-Type Graph`. Para MS Graph, use `-Entity` para especificar que tipo de recurso está a esperar. As entidades disponíveis são `Usuário`, `Grupo`, `Dispositivo`. Isto produzirá um seletor para utilizadores, grupos ou dispositivos no Entra ID dado.

O seletor inclui uma pesquisa rápida, para localizar facilmente o recurso necessário.

![Exemplo de Seletor](/files/fd8e7a08510ba33d9ad8bc27ed96b9401be94849)

Atualmente, não é possível multisseleção usando um seletor.

Por padrão, um seletor do MS Graph devolverá o ID do objeto. Se precisar, por exemplo, do nome principal do utilizador em vez disso, certifique-se de incluir "name" como sufixo no nome da variável. Portanto, basicamente, para obter o id de um utilizador, nomeie o parâmetro `$userid`. Se quiser um UPN, nomeie-o `$username`.

#### Filtragem do Graph

Se estiver a usar um seletor baseado em MS Graph, também pode especificar `-Filter` e usar um [filtro ODATA](https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0\&view=graph-rest-1.0#filter-parameter) para limitar os objetos oferecidos no seletor.

O exemplo seguinte listará apenas grupos do Entra ID que começam com "LIC\_".

```powershell
param(
    [Parameter(Mandatory = $true)]
    [ValidateScript( { Use-RJInterface -Type Graph -Entity Group -Filter "startswith(DisplayName, 'LIC_')" -DisplayName "Grupo de licença" } )]
    [String] $GroupID_License
)
```

Pode preparar filtros e reutilizá-los em vários scripts usando o [repositório central de dados](#graph-filters). Neste caso, basta referenciar o filtro pelo nome usando `-Filter "ref:LicenseGroup"`, onde `ref:` indica procurar um filtro armazenado.

```powershell
param(
    [Parameter(Mandatory = $true)]
    [ValidateScript( { Use-RJInterface -Type Graph -Entity Group -Filter "ref:LicenseGroup" } )]
    [String] $GroupID_License
)
```

Este exemplo específico `ref:LicenseGroup` está disponível por padrão sem qualquer configuração adicional.

![filtro ODATA](/files/af67c2f470082ec3fe983dfbd473ab0da74f6b0f)

## Cabeçalho do Runbook

O Portal pode analisar a secção de [ajuda baseada em comentários](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help?view=powershell-5.1) de um runbook, se estiver presente.

Aqui está um exemplo:

```powershell
<#
  .SYNOPSIS
  (Des-)atribuir uma licença a um utilizador através da associação a um grupo.

  .DESCRIPTION
  (Des-)atribuir uma licença a um utilizador através da associação a um grupo. Descrição mais detalhada...

  .PARAMETER DefaultGroups
  Lista separada por vírgulas de grupos a atribuir. por ex. "DL Sales,LIC Internal Product"

  .NOTES
  Permissões:
  MS Graph (API):
  - User.Read.All
  - GroupMember.ReadWrite.All 
  - Group.ReadWrite.All

  .INPUTS
  RunbookCustomization: {
        "Parameters": {
            "UserName": {
                "Hide": true
            },
            "Remove": {
                "DisplayName": "Atribuir ou Remover Licença",
                "SelectSimple": {
                    "Assign License to User": false,
                    "Remove License from User": true
                }
            }
        }
    }
#>
```

`.SYNOPSIS` - Dê uma descrição muito breve da função do seu runbook. Isto será apresentado na lista de runbooks disponíveis.

`.DESCRIPTION` - Dê uma descrição da função do seu runbook. Pode conter um pouco mais de detalhe, pois isto será apresentado dentro da execução / diálogo de parâmetros dos runbooks.

`.PARAMETER` - Precisa ser seguido pelo nome de um parâmetro. Permite fornecer uma explicação detalhada da entrada esperada para o parâmetro em questão.

`.INPUTS` - Pode conter um bloco de personalização de Runbook baseado em JSON.

`.NOTES` - Não é analisado / renderizado. Use este espaço para registar quais permissões e requisitos existem para o seu runbook.

`.EXAMPLE` - Não é analisado / renderizado. Pode conter um exemplo de personalização baseada em JSON para usar no repositório de dados do RealmJoin no seu Tenant. Estes podem ser exemplos de como criar modelos, por exemplo, para diferentes fluxos de trabalho ou classes de utilizadores.

## Personalização Baseada em JSON

### Repositório de Dados Central

Cada Tenant Azure pode alojar um repositório de dados "Runbook Customizations", encontrado em <https://portal.realmjoin.com/settings/runbooks-customizations> .

O formato é JSON com comentários, permitindo vírgulas finais. Atualmente, existem três secções relevantes, `Settings`, `Modelos`, `Runbooks`.

```json
{
    "Settings": {
    },
    "Templates": {
    },
    "Runbooks": {
    }
}
```

### secção Runbooks

`Runbooks` é analisada pelo portal ao iniciar um runbook. Se existir uma secção com o nome do atual Azure Automation Runbook, o seu conteúdo será usado para personalizar o frontend apresentado ao utilizador.

Suponha o seguinte runbook simples de demonstração, chamado `rjgit-device_demo-runbook-customizing`.

```powershell
<#
  .SYNOPSIS
  Demonstrar Personalização de Runbook

  .DESCRIPTION
  Demonstrar Personalização de Runbook, como lista suspensa/seletor
#>

#Requires -Modules @{ModuleName = "RealmJoin.RunbookHelper"; ModuleVersion = "0.6.0" }

param(
    [string] $DeviceId,
    [bool] $ExtraWorkflow = $true,
    [int] $ExtraWorkflowTime = 15
)

"## A fazer coisas ao dispositivo '$DeviceID'"

# Fluxo de trabalho complicado altamente opcional
if ($ExtraWorkflow) {
    "## A executar Meditação..."
    Start-Sleep -Seconds $ExtraWorkflowTime
}
```

Se não for personalizado, será apresentado assim no frontend:

![Demonstração - antes](/files/1bbf7a0209bf21af95d610cb26d298976c221f5c)

Pensamentos:

* Como este runbook é iniciado a partir do contexto de um dispositivo no portal, o `$DeviceId` é informação redundante para um utilizador. Já sei em que dispositivo estou a trabalhar.
* O que acontece se eu ativar ou desativar o "Fluxo de Trabalho Extra"? Preciso de pensar no "Tempo do Fluxo de Trabalho Extra" se eu desativar o "Fluxo de Trabalho Extra"?

Vamos melhorar isso. O seguinte JSON de exemplo no repositório de dados central modificará a UI do runbook.

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "ParameterList": [
                {
                    "Name": "DeviceId",
                    "Hide": true
                }, 
                {
                    "Name": "ExtraWorkflow",
                    "Hide": true
                },
                {
                    "Name": "ExtraWorkflowTime",
                    "DisplayName": "Quanto tempo para meditar?",
                },
                {
                    "DisplayName": "Executar Fluxo de Trabalho Extra",
                    "DisplayBefore": "ExtraWorkflowTime",
                    "Select": {
                        "Options": [
                            {
                                "Display": "Executar Meditação (opcional)",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": true
                                    }
                                }
                            },
                            {
                                "Display": "Ignorar Mindfulness do Dispositivo",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": false
                                    },
                                    "Hide": [
                                        "ExtraWorkflowTime"
                                    ]
                                }
                            }
                        ],
                        
                    },
                    "Default": "Ignorar Mindfulness do Dispositivo"
                }
            ]
        }
    }
}
```

Pode usar a mesma notação / funcionalidades no seu [cabeçalho do runbook](#runbook-header).

#### ParameterList

Cada parâmetro tem a sua própria secção em `ParameterList`. [Modificadores](#modifiers) permitem alterar o comportamento desse parâmetro.

O resultado será semelhante a isto:

![Demonstração - após ocultado](/files/13056fe34a28a7dd86d5a404c992058d0fcfbf5a)

Escolher o fluxo de trabalho adicional apresentará (desocultará) mais parâmetros:

![Demonstração - após desocultado](/files/0fade93442f6dcac041b2e83099d5de301ceabd5)

Isto mostra menos desorganização em comparação com antes de aplicar a personalização. Ao mesmo tempo, mais informação sobre as alternativas de "Fluxo de Trabalho Extra" está disponível para o utilizador. Além disso, o utilizador só terá de se preocupar com "Tempo do Fluxo de Trabalho Extra" se isso for relevante.

Alterar a visibilidade desse campo foi feito usando um `"Customization"` bloco dentro de uma das opções de `"Select"` Pode atualmente ter no máximo um bloco destes ativo de cada vez. `"Customization"` Como pode ver, o parâmetro

está completamente oculto. Isto é feito definindo o `$DeviceId` para este parâmetro. `"Hide": true` Os parâmetros podem ter um

. Oferecemos um `DisplayName`amigável para substituir `DisplayName` $ExtraWorkflowTime `na UI. Veja outros` modificadores [para mais informações.](#modifiers) Pode inserir parâmetros "sem nome" (faltando a declaração

statement) como a secção "Executar Fluxo de Trabalho Extra", se quiser oferecer elementos de UI sem devolver diretamente um valor. Normalmente, isto só é usado em conjunto com `Nome` } `Selecione`.

#### Selecione

Usamos `Selecione`para exibir uma lista de `Opções` num menu suspenso. Cada opção pode `Apresentação` texto ou acionar uma `Personalização`como definir `Ocultar` ou um `Padrão` valor em outros parâmetros. No nosso exemplo, usamos isso para (des)ocultar `na UI. Veja outros` e substituir `$ExtraWorkflow`valor.

`na UI. Veja outros` é, assim, mostrado apenas quando relevante e o alternador binário `$ExtraWorkflow` agora é substituído por alternativas com significado do ponto de vista do usuário.

No caso de uma `Selecione` para um parâmetro nomeado, cada opção deve ter um `"ParameterValue": "..."` a ser passado ao runbook. Você pode colocar um `"ShowValue: false"` dentro do bloco `Selecione` para mostrar apenas o menu suspenso e não um campo para o valor resultante do parâmetro.

Exemplo de parâmetro nomeado:

```json
{
    "Name": "ExtraWorkflow",
    "DefaultValue": true,
    "DisplayName": "Executar Fluxo de Trabalho Extra",
    "DisplayBefore": "ExtraWorkflowTime",
    "Select": {
        "Options": [
            {
                "Display": "Executar Meditação (opcional)",
                "ParameterValue": true
            },
            {
                "Display": "Ignorar Mindfulness do Dispositivo",
                "ParameterValue": false,
                "Customization": {
                    "Hide": [
                        "ExtraWorkflowTime"
                    ]
                }
            }
        ],
        "ShowValue": false
    }
}
```

O `Padrão` / `DefaultValue` no parâmetro também especifica o estado inicial do menu suspenso. No caso de um parâmetro sem nome, use o `DisplayName` da opção desejada; caso contrário, forneça um valor de retorno padrão, como "true" ou "false" ou alguma string.

#### Parâmetros

Se você tiver apenas parâmetros nomeados, pode usar o formato ligeiramente mais curto `Parâmetros` em vez de `ParameterList`.

Para um exemplo, veja SelectSimple

#### SelectSimple

Se o poder total de um `Selecione` não for necessário e você apenas quiser oferecer uma lista de valores possíveis em um menu suspenso (sem aplicar personalizações adicionais), você pode usar `SelectSimple`.

`SelectSimple` só é utilizável para parâmetros nomeados.

Exemplo:

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "Parameters": {
                "DeviceId": {
                    "Hide": true
                }, 
                "ExtraWorkflow": {
                    "Name": "ExtraWorkflow",
                    "DisplayName": "Executar Fluxo de Trabalho Extra",
                    "Default": false,
                    "SelectSimple": {
                        "Execute Meditation (optional)": true,
                        "Skip Device Mindfulness": false
                    }
                },
                "ExtraWorkflowTime": {
                    "DisplayName": "Quanto tempo meditar?"
                }
            }
        }
    }
}
```

A maior diferença (além de ser muito mais curto) em relação ao nosso exemplo anterior é que `na UI. Veja outros` está sempre visível.

#### Modificadores

Cada parâmetro pode ter um ou mais dos seguintes modificadores:

* `"DisplayName": "text"` - Exibir "text" como nome do parâmetro na interface
* `"Hide": true / false` - Ocultar este parâmetro
* `"Mandatory": true / false` - Exigir que este parâmetro seja preenchido
* `"ReadOnly": true / false` - Impedir que este parâmetro seja alterado em relação ao seu valor padrão
* `"DefaultValue": "..."` - Definir um valor padrão para este parâmetro. (Você também pode usar `Padrão` em vez disso.)
* `"GraphFilter": "startswith(DisplayName, 'LIC_')"` - veja [Filtragem do Graph](#graph-filtering)
* `"AllowEdit": true / false` - Impedir a edição manual deste parâmetro. (combine isso com templates)

### Settings

`Settings` permite armazenar dados de configuração como nomes de Azure Storage Account em um local central, mantendo-os separados dos seus runbooks.

Você pode acessar valores individuais de um bloco de parâmetros de um runbook usando `Use-RJInterface`.

Vamos usar este exemplo de bloco de parâmetros de um runbook:

```powershell
param(
    [ValidateScript( { Use-RJInterface -Type Setting -Attribute "CaPoliciesExport.Container" } )]
    [string] $ContainerName,
    [ValidateScript( { Use-RJInterface -Type Setting -Attribute "CaPoliciesExport.ResourceGroup" } )]
    [string] $ResourceGroupName,
    [ValidateScript( { Use-RJInterface -Type Setting -Attribute "CaPoliciesExport.StorageAccount.Name" } )]
    [string] $StorageAccountName,
    [ValidateScript( { Use-RJInterface -Type Setting -Attribute "CaPoliciesExport.StorageAccount.Location" } )]
    [string] $StorageAccountLocation,
    [ValidateScript( { Use-RJInterface -Type Setting -Attribute "CaPoliciesExport.StorageAccount.Sku" } )]
    [string] $StorageAccountSku
)
```

O Portal tentará preencher previamente cada parâmetro com valores do armazenamento central de dados - se estiverem presentes. Isso também funciona se o parâmetro tiver sido ocultado na interface.

Um possível JSON no armazenamento de dados para este runbook seria:

```json
{
    "Settings": {
        "CaPoliciesExport": {
            "ResourceGroup": "rj-runbooks-01",
            "StorageAccount": {
                "Name": "rjrbexports01",
                "Location": "West Europe",
                "Sku": "Standard_LRS"
            }
        }
    }
}
```

O elemento `Container` em falta simplesmente não será preenchido previamente na interface.

### Modelos

`Modelos` use referências JSON para carregar dados - por exemplo, uma longa lista de localizações de escritórios - ao usar uma `Selecione` declaração.

Isso permite manter uma personalização neutra/reutilizável/separada dos dados reais.

Vamos tomar o exemplo de integrar novos usuários. Você pode ter várias opções dadas para departamentos ou localizações de escritórios, em que atribuir uma localização de escritório também exige um determinado endereço, país, estado etc.

O exemplo a seguir de uma personalização de runbook usa a `$ref` dentro do bloco `Runbooks` seção para referenciar/importar uma subárvore da seção `Modelos` . Procure as palavras-chave `$id`/`$values` . Tenha em mente que elas `$id`/`$values` têm de ser definidas antes de serem referenciadas usando `$ref`. É por isso que `Modelos` está definido antes de `Runbooks` neste exemplo.

Neste exemplo, dizemos ao Portal para pegar a subárvore com o `$id` chamada `LocationOptions` e incluir seus `$values`, substituindo a declaração `$ref` . Assim, o Portal renderizará um `Selecione` conforme descrito na seção `Runbooks` mas incluirá as opções reais de `Modelos`.

Um template pode conter qualquer declaração que seja suportada no local de referência. Neste exemplo, usamos uma declaração `Personalização` para modificar outros parâmetros como `StreetAddress`.

Assim, podemos ter uma personalização específica de um runbook em `Runbooks` reutilizável em vários ambientes, mantendo os dados reais separados.

```json
{
    "Templates": {
        "Options": [
            {
                "$id": "LocationOptions",
                "$values": [
                    {
                        "Display": "DE-OF",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Kaiserstraße 39",
                                "PostalCode": "63065",
                                "City": "Offenbach",
                                "Country": "Germany"
                            }
                        }
                    },
                    {
                        "Display": "DE-DEG",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Lateinschulgassse 24-26",
                                "PostalCode": "94469",
                                "City": "Deggendorf",
                                "Country": "Germany"
                            }
                        }
                    },
                    {
                        "Display": "DE-HH",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Hans-Henny-Jahnn-Weg 53",
                                "PostalCode": "22085",
                                "City": "Hamburg",
                                "Country": "Germany"
                            }
                        }
                    },
                    {
                        "Display": "FI-HS",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Somewhere 42",
                                "PostalCode": "12345",
                                "City": "Helsinki",
                                "Country": "Finland"
                            }
                        }
                    }
                ]
            },
            {
                "$id": "CompanyOptions",
                "$values": [
                    {
                        "Id": "gkg",
                        "Display": "glueckkanja",
                        "Value": "glueckkanja AG"
                    },
                    {
                        "Id": "pp",
                        "Display": "PRIMEPULSE",
                        "Value": "PRIMEPULSE SE"
                    }
                ]
            }
        ]
    },
    "Runbooks": {
        "rjgit-org_general_add-user": {
            "ParameterList": [
                {
                    "DisplayName": "Localização do escritório",
                    "DisplayAfter": "CompanyName",
                    "Select": {
                        "Options": {
                            "$ref": "LocationOptions"
                        }
                    }
                },
                {
                    "Name": "CompanyName",
                    "Select": {
                        "Options": {
                            "$ref": "CompanyOptions"
                        },
                        "AllowEdit": false
                    }
                }
            ],
            "ReadOnly": [
                "StreetAddress",
                "PostalCode",
                "City",
                "Country"
            ]
        }
    }
}
```

Isso criará a seguinte interface:

![Demo - ref-location](/files/717595c867fde31c038d5dfdaae7050099a19769)

![Demo - ref-address](/files/e4bdfb7b2cfc908c189034499e2393bf9c868b7d)

### Filtros Graph

Você pode preparar [Filtros Graph ODATA](https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0\&view=graph-rest-1.0#filter-parameter) para serem usados em vários runbooks. Armazene-os em uma seção chamada `GraphFilters`.

O exemplo a seguir filtra um determinado prefixo no `DisplayName` de um grupo, para mostrar apenas grupos relacionados a licenciamento em um seletor de grupos.

```json
"GraphFilters": {
    "LicenseGroup": "startswith(DisplayName, 'LIC_')" // também contido no código RJ como padrão
  }
```

Ver [Filtragem Graph](#graph-filtering) sobre como usar isso a partir de um runbook.


---

# Agent Instructions: 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:

```
GET https://docs.realmjoin.com/pt/automacao/runbooks/runbook-customization.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
