# Personalização de Runbooks

## Vista geral

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:

* Alojar parâmetros e modelos específicos do cliente/tenant
* Oferecer elementos de IU como seletores de utilizador ou seleções em menus suspensos
* Apresentar explicações legíveis para humanos dos parâmetros
* Ocultar elementos de IU desnecessários

<figure><img src="https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2FmVmZrDFgG1ShkZlo1Fbe%2Fimage.png?alt=media&#x26;token=39876732-3578-41d2-acf1-8571bb941e68" 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 predefinição, tentaremos oferecer predefinições sensatas nos runbooks disponibilizados em [GitHub](https://github.com/realmjoin/realmjoin-runbooks).

Alguns runbooks virão com exemplos de como configurar modelos específicos do cliente, como indicar localizações de escritório para o processo de integração do utilizador.

### Formatar

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

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

Adicionalmente (com a 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 ao especificar um tipo de dados no bloco de parâmetros. Pode combinar vários tipos de personalizações para obter os 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 apresentar. Sempre que possível, também validará as entradas de acordo com o tipo .NET dado para uma variável.

Atualmente, são compreendidos os seguintes tipos de dados:

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

Pode aplicar modificadores standard do PowerShell aos parâmetros. O RealmJoin Portal, em particular, compreenderá se especificar `[Parameter(Mandatory = $true)]` para indicar um parâmetro obrigatório e impor que estes parâmetros sejam preenchidos.

Sempre que possível, o RealmJoin Portal também lerá e apresentará valores predefinidos fornecidos na IU.

Tenha em atenção que os valores predefinidos 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 Runbook Helper do RealmJoin no seu runbook:

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

Pode então incluir `[ValidateScript( { Use-RJInterface ... } )]` instruções nas definições dos 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 peça por peça. `[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 [módulo PowerShell RealmJoin Runbook Helper](https://github.com/realmjoin/RealmJoin.RunbookHelper) Permite-lhe 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-lhe passar ao RealmJoin Portal uma sugestão / 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 o MS Graph, use `-Entity` para especificar que tipo de recurso está à espera. As entidades disponíveis são `Usuário`, `Grupo`, `Dispositivo`. Isto produzirá um seletor para utilizadores, grupos ou dispositivos no Entra ID indicado.

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

![Exemplo de Seletor](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-0c393c4ea73a77b34da2c678b0712a6556ed3bc2%2Frunbook-customization-img2.png?alt=media)

Atualmente, não é possível selecionar múltiplos itens usando um seletor.

Por predefinição, um seletor 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. Assim, 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 apresentados no seletor.

O exemplo seguinte listará apenas grupos do Entra ID que comecem por "LIC\_".

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

Pode preparar filtros e reutilizá-los em vários scripts usando a [base de dados central](#graph-filters). Neste caso, basta referenciar o filtro pelo nome usando `-Filter "ref:LicenseGroup"`, onde `ref:` indica que deve 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 predefinição, sem necessidade de configuração adicional.

![Filtro ODATA](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-4913ddfbc3324fc7a3522de2bc6887e8d2745e35%2Frunbook-customization-img3.png?alt=media)

## Cabeçalho do Runbook

O Portal pode analisar a secção [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 esta existir.

Aqui está um exemplo:

```powershell
<#
  .SYNOPSIS
  Atribuir (ou remover a atribuição de) uma licença a um utilizador através da associação a grupos.

  .DESCRIPTION
  Atribuir (ou remover a atribuição de) uma licença a um utilizador através da associação a grupos. Descrição mais detalhada...

  .PARAMETER DefaultGroups
  Lista separada por vírgulas de grupos a atribuir. p. 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 será apresentada dentro do diálogo de execução / parâmetros dos runbooks.

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

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

`.NOTES` - Não é analisado / apresentado. Utilize este espaço para anotar quais permissões e requisitos existem para o seu runbook.

`.EXAMPLE` - Não é analisado / apresentado. Pode conter um exemplo de uma personalização baseada em JSON para usar no Datastore do RealmJoin no seu tenant. Estes podem ser exemplos de como criar modelos, por exemplo, para diferentes fluxos de trabalho ou classes de utilizador.

## Personalização Baseada em JSON

### Base de Dados Central

Cada tenant Azure pode alojar uma base de dados "Runbook Customizations", encontrada 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, `Definições`, `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 Runbook do Azure Automation, o seu conteúdo será usado para personalizar o frontend apresentado ao utilizador.

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

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

  .DESCRIPTION
  Demonstrar Personalização de Runbook, como dropdown/select
#>

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

![Demo - before](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-931b958f4b949268736f6ff3f174913cf56adcf7%2Frunbook-customization-img4.png?alt=media)

Pensamentos:

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

Vamos melhorar isso. O seguinte JSON de exemplo na base de dados central modificará a IU para o runbook.

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "ParameterList": [
                {
                    "Name": "DeviceId",
                    "Hide": true
                }, 
                {
                    "Name": "ExtraWorkflow",
                    "Hide": true
                },
                {
                    "Name": "ExtraWorkflowTime",
                    "DisplayName": "Durante quanto tempo meditar?",
                },
                {
                    "DisplayName": "Executar Fluxo de Trabalho Adicional",
                    "DisplayBefore": "ExtraWorkflowTime",
                    "Select": {
                        "Options": [
                            {
                                "Display": "Executar Meditação (opcional)",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": true
                                    }
                                }
                            },
                            {
                                "Display": "Saltar a Atenção Plena do Dispositivo",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": false
                                    },
                                    "Hide": [
                                        "ExtraWorkflowTime"
                                    ]
                                }
                            }
                        ],
                        
                    },
                    "Default": "Saltar a Atenção Plena 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á assim:

![Demo - after hidden](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-4ae4e681371ad29f076063c5f1925a0cb8113872%2Frunbook-customization-img5.png?alt=media)

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

![Demo - after unhidden](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-7960cf8309d8bbdf12ab1539c073714211715411%2Frunbook-customization-img6.png?alt=media)

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 adicional" fica disponível para o utilizador. Além disso, o utilizador só terá de se preocupar com o "Tempo do fluxo de trabalho adicional" se este 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 desses blocos `"Customization"` ativo de cada vez.

Como pode ver, o parâmetro `$DeviceId` está completamente oculto. Isto é feito definindo o `"Hide": true` para este parâmetro.

Os parâmetros podem ter um `DisplayName`. Oferecemos um `DisplayName` mais amigável para substituir `$ExtraWorkflowTime` na IU. Veja outros [modificadores](#modifiers) para mais informações.

Pode inserir parâmetros "sem nome" (faltando a instrução `Nome` como a secção "Executar Fluxo de Trabalho Adicional", se quiser oferecer elementos de IU sem devolver diretamente um valor. Isto normalmente só é usado em conjunto com `Selecione`.

#### Selecione

Usámos `Selecione`, para apresentar uma lista de `Opções` num menu suspenso. Cada opção pode `Apresentação` texto, ou desencadear um `Personalização`, como definir `Hide` ou um valor `Default` noutros parâmetros. No nosso exemplo, usámo-lo para (des)ocultar `$ExtraWorkflowTime` e substituir `$ExtraWorkflow`o valor.

`$ExtraWorkflowTime` é, assim, apresentado apenas quando relevante e o interruptor binário `$ExtraWorkflow` é agora substituído por alternativas com significado do ponto de vista do utilizador.

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

Exemplo de parâmetro nomeado:

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

O `Default` / `DefaultValue` instrução 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 pretendida, caso contrário dê um valor de retorno predefinido, como "true" ou "false" ou alguma string.

#### Parâmetros

Se tiver apenas parâmetros nomeados, pode usar o formato um pouco 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 apenas quiser oferecer uma lista de valores possíveis num menu suspenso (sem aplicar personalização adicional), pode usar `SelectSimple`.

`SelectSimple` só pode ser usado para parâmetros nomeados.

Exemplo:

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "Parameters": {
                "DeviceId": {
                    "Hide": true
                }, 
                "ExtraWorkflow": {
                    "Name": "ExtraWorkflow",
                    "DisplayName": "Executar Fluxo de Trabalho Adicional",
                    "Default": false,
                    "SelectSimple": {
                        "Executar Meditação (opcional)": true,
                        "Saltar a Atenção Plena do Dispositivo": false
                    }
                },
                "ExtraWorkflowTime": {
                    "DisplayName": "Durante quanto tempo meditar?"
                }
            }
        }
    }
}
```

A maior diferença (para além de ser muito mais curto) em relação ao nosso exemplo anterior é que `$ExtraWorkflowTime` é sempre visível.

#### Modificadores

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

* `"DisplayName": "texto"` - Apresenta "texto" como nome do parâmetro na IU
* `"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 a partir do seu valor predefinido
* `"DefaultValue": "..."` - Definir um valor predefinido para este parâmetro. (Também pode usar `Default` 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 isto com modelos)

### Definições

`Definições` permite-lhe armazenar dados de configuração como nomes de Azure Storage Account num local central, mantendo-os ainda assim separados dos seus runbooks.

Pode aceder a valores individuais a partir do bloco de parâmetros de um runbook usando `Use-RJInterface`.

Vamos tomar 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á pré-preencher cada parâmetro com valores da base de dados central - se estiverem presentes. Isto também funciona se o parâmetro tiver sido ocultado na IU.

Um possível JSON na base 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á pré-preenchido na IU.

### Modelos

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

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

Vamos tomar o exemplo da integração de novos utilizadores. Poderá ter várias opções para departamentos ou localizações de escritório, em que atribuir uma localização de escritório também exige um determinado endereço de rua, país, estado, etc.

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

Neste exemplo, dizemos ao portal para obter a subárvore com o `$id` chamado `LocationOptions` e incluir os seus `$values`, substituindo a instrução `$ref` . Assim, o portal irá apresentar um `Selecione` conforme descrito na secção `Runbooks` mas incluir as opções reais de `Modelos`.

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

Assim, podemos ter uma personalização específica do runbook em `Runbooks` reutilizável entre 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": "Alemanha"
                            }
                        }
                    },
                    {
                        "Display": "DE-DEG",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Lateinschulgassse 24-26",
                                "PostalCode": "94469",
                                "City": "Deggendorf",
                                "Country": "Alemanha"
                            }
                        }
                    },
                    {
                        "Display": "DE-HH",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Hans-Henny-Jahnn-Weg 53",
                                "PostalCode": "22085",
                                "City": "Hamburgo",
                                "Country": "Alemanha"
                            }
                        }
                    },
                    {
                        "Display": "FI-HS",
                        "Customization": {
                            "Default": {
                                "StreetAddress": "Somewhere 42",
                                "PostalCode": "12345",
                                "City": "Helsínquia",
                                "Country": "Finlândia"
                            }
                        }
                    }
                ]
            },
            {
                "$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"
            ]
        }
    }
}
```

Isto criará a seguinte IU:

![Demo - ref-location](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-93295541748dcc12dbc72208851138efd0808d51%2Frunbook-customization-img7.png?alt=media)

![Demo - ref-address](https://686519968-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MkrcM7cKOpXKri1kVrh%2Fuploads%2Fgit-blob-11e2812670e56cf10bb03fe7588059d232a8addf%2Frunbook-customization-img8.png?alt=media)

### Filtros do Graph

Pode preparar [Filtros ODATA do Graph](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 numa secção chamada `GraphFilters`.

Os seguintes exemplos filtram por um determinado prefixo no `DisplayName` de um grupo, para mostrar apenas grupos relacionados com licenciamento num seletor de grupos.

```json
"GraphFilters": {
    "LicenseGroup": "startswith(DisplayName, 'LIC_')" // também incluído no código RJ como predefinição
  }
```

Consulte [Filtragem do Graph](#graph-filtering) sobre como usar isto a partir de um runbook.
