# Personalización de runbooks

## Información general

La implementación de runbooks de RealmJoin ofrece capacidades de personalización al autor de un runbook o al administrador de un entorno, de modo que puedan:

* Alojar parámetros y plantillas específicos del cliente/tenant
* Ofrecer elementos de la interfaz de usuario como selectores de usuarios o selecciones desplegables
* Presentar explicaciones legibles por humanos de los parámetros
* Ocultar elementos de la interfaz de usuario no necesarios

<figure><img src="https://3832142177-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>

Las personalizaciones pueden incluirse en el propio runbook y/o almacenarse en la instancia del Portal RealmJoin del cliente. De forma predeterminada, intentaremos ofrecer valores predeterminados sensatos en los runbooks ofrecidos en [GitHub](https://github.com/realmjoin/realmjoin-runbooks).

Algunos runbooks incluirán ejemplos de cómo configurar plantillas específicas del cliente, como especificar ubicaciones de oficina para la incorporación de usuarios.

### Formato

La personalización puede definirse (en orden descendente de prioridad)

* Bloque de JSON en [configuración del Portal RealmJoin](https://portal.realmjoin.com/settings/runbooks-customizations), anulando el comportamiento predeterminado del runbook
* Bloque de JSON en la cabecera de un runbook

Además (con la menor prioridad)

* por parámetro en la cabecera del runbook
* por parámetro en el bloque de parámetros de los runbooks (usando el módulo auxiliar RJRb)

Algunas funcionalidades (como las plantillas) solo están disponibles en formato JSON. Algunas funcionalidades (como crear un selector de usuarios) solo están disponibles especificando un tipo de dato en el bloque de parámetros. Puede combinar varios tipos de personalizaciones para obtener mejores resultados.

## Bloque de parámetros del runbook

El Portal RealmJoin analiza el bloque de parámetros PowerShell de un runbook para determinar qué campos de entrada mostrar. Cuando es posible, también validará las entradas según el tipo .NET dado para una variable.

Los siguientes tipos de datos se entienden actualmente:

* `[bool]`, `[boolean]` - presentará un conmutador binario
* `[string]` - presentará un cuadro de texto para escribir cualquier entrada alfanumérica
* `[int]` - presentará un cuadro de texto, permitiendo solo entradas numéricas
* `[DateTime]`, `[DateTimeOffset]` - presentará un selector de fecha/hora

Puede aplicar modificadores estándar de PowerShell a los parámetros. El Portal RealmJoin, en particular, entenderá si especifica `[Parameter(Mandatory = $true)]` para indicar un parámetro obligatorio y exigir que estos parámetros se rellenen.

Cuando sea posible, el Portal RealmJoin también leerá y mostrará en la interfaz los valores predeterminados proporcionados.

Tenga en cuenta que los valores predeterminados del runbook pueden ser anulados por personalizaciones. Además, los parámetros pueden ocultarse completamente mediante personalizaciones.

### Personalizar parámetros

Para poder personalizar parámetros, asegúrese de incluir el módulo PS RealmJoin Runbook Helper en su runbook:

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

Entonces puede incluir `[ValidateScript( { Use-RJInterface ... } )]` declaraciones en las definiciones de parámetros. Por ejemplo, lo siguiente creará un selector de usuarios, permitiendo elegir un usuario de Entra ID y pasará su id de objeto como cadena al runbook.

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

Analicemos esto paso a paso. `[ValidateScript...]` es un modificador para el siguiente parámetro definido en el bloque param. En este caso, la variable `$AssignedUserId`.

`Use-RJInterface` es parte de nuestro [RealmJoin Runbook Helper](https://github.com/realmjoin/RealmJoin.RunbookHelper) módulo de PowerShell. Le permite especificar qué tipo de entrada espera usando `-Type` y `-Entity`, si eso no está ya definido completamente por el tipo de variable.

`-DisplayName` le permite pasar a RealmJoin Portal un mensaje/prompt o descripción legible por humanos para este parámetro.

#### Recursos de Graph

En el ejemplo anterior, la fuente de información es MS Graph, como se describe con `-Type Graph`. Para MS Graph, use `-Entity` para especificar qué tipo de recurso está esperando. Las entidades disponibles son `usuario`, `Grupo`, `Dispositivo`. Esto producirá un selector para usuarios, grupos o dispositivos en el Entra ID dado.

El selector incluye una búsqueda rápida para localizar fácilmente el recurso requerido.

![Ejemplo de selector](https://3832142177-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)

Actualmente, no es posible la multiselección usando un selector.

De forma predeterminada, un selector de MS Graph devolverá el id del objeto. Si necesita, por ejemplo, el nombre principal de usuario en su lugar, asegúrese de incluir "name" como sufijo en el nombre de su variable. Así que, básicamente, para obtener el id de un usuario, nombre el parámetro `$userid`. Si quiere un UPN, nómbrelo `$username`.

#### Filtrado de Graph

Si está usando un selector basado en MS Graph, también puede especificar `-Filter` y usar un [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 los objetos ofrecidos en el selector.

El siguiente ejemplo mostrará solo grupos de Entra ID que comiencen con "LIC\_".

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

Puede preparar filtros y reutilizarlos en varios scripts usando el [almacén de datos central](#graph-filters). En este caso, solo haga referencia al filtro por nombre usando `-Filter "ref:LicenseGroup"`, donde `ref:` indica que se debe buscar un filtro almacenado.

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

Este ejemplo específico `ref:LicenseGroup` está disponible de forma predeterminada sin necesidad de configuración adicional.

![filtro ODATA](https://3832142177-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)

## Cabecera del runbook

El Portal puede analizar la [sección de ayuda basada en comentarios](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_comment_based_help?view=powershell-5.1) de un runbook, si está presente.

Aquí hay un ejemplo:

```powershell
<#
  .SYNOPSIS
  Asignar o desasignar una licencia a un usuario mediante la pertenencia a un grupo.

  .DESCRIPTION
  Asignar o desasignar una licencia a un usuario mediante la pertenencia a un grupo. Descripción más detallada...

  .PARAMETER DefaultGroups
  Lista separada por comas de grupos a asignar. p. ej. "DL Sales,LIC Internal Product"

  .NOTES
  Permisos:
  MS Graph (API):
  - User.Read.All
  - GroupMember.ReadWrite.All 
  - Group.ReadWrite.All

  .INPUTS
  RunbookCustomization: {
        "Parameters": {
            "UserName": {
                "Hide": true
            },
            "Remove": {
                "DisplayName": "Asignar o quitar licencia",
                "SelectSimple": {
                    "Assign License to User": false,
                    "Remove License from User": true
                }
            }
        }
    }
#>
```

`.SYNOPSIS` - Dé una descripción muy breve de la función de su runbook. Esto se mostrará en la lista de runbooks disponibles.

`.DESCRIPTION` - Dé una descripción de la función de su runbook. Puede contener un poco más de detalle, ya que esto se mostrará dentro del cuadro de diálogo de ejecución/parámetros de los runbooks.

`.PARAMETER` - Debe ir seguido del nombre de un parámetro. Le permite dar una explicación detallada de la entrada esperada para el parámetro en cuestión.

`.INPUTS` - Puede contener un bloque de personalización de runbook basado en JSON.

`.NOTES` - No se analiza ni se renderiza. Use este espacio para anotar qué permisos y requisitos existen para su runbook.

`.EXAMPLE` - No se analiza ni se renderiza. Puede contener un ejemplo de una personalización basada en JSON que usar en el almacén de datos RealmJoin de su tenant. Estos pueden ser ejemplos de cómo crear plantillas, por ejemplo, para distintos flujos de trabajo o clases de usuario.

## Personalización basada en JSON

### Almacén de datos central

Cada tenant de Azure puede alojar un almacén de datos de "Runbook Customizations", que se encuentra en <https://portal.realmjoin.com/settings/runbooks-customizations> .

El formato es JSON con comentarios, permitiendo comas finales. Actualmente, hay tres secciones relevantes, `Configuración`, `Plantillas`, `Runbooks`.

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

### Sección de runbooks

`Runbooks` es analizada por el portal al iniciar un runbook. Si existe una sección con el nombre del actual Azure Automation Runbook, su contenido se utilizará para personalizar la interfaz mostrada al usuario.

Suponga el siguiente runbook simple de demostración, llamado `rjgit-device_demo-runbook-customizing`.

```powershell
<#
  .SYNOPSIS
  Demostrar personalización de runbooks

  .DESCRIPTION
  Demostrar personalización de runbooks, como desplegable/selección
#>

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

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

"## Haciendo cosas con el dispositivo '$DeviceID'"

# Flujo de trabajo complicado altamente opcional
if ($ExtraWorkflow) {
    "## Ejecutando meditación..."
    Start-Sleep -Seconds $ExtraWorkflowTime
}
```

Si no se personaliza, se presentará así en la interfaz:

![Demo - antes](https://3832142177-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)

Pensamientos:

* Como este runbook se inicia desde el contexto de un dispositivo en el portal, el `$DeviceId` es información redundante para un usuario. Ya sé en qué dispositivo estoy trabajando.
* ¿Qué ocurre si habilito o deshabilito el "Extra Workflow"? ¿Necesito pensar en "Extra Workflow Time" si deshabilito "Extra Workflow"?

Vamos a mejorarlo. El siguiente JSON de ejemplo en el almacén de datos central modificará la interfaz de usuario para el runbook.

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "ParameterList": [
                {
                    "Name": "DeviceId",
                    "Hide": true
                }, 
                {
                    "Name": "ExtraWorkflow",
                    "Hide": true
                },
                {
                    "Name": "ExtraWorkflowTime",
                    "DisplayName": "¿Cuánto tiempo meditar?",
                },
                {
                    "DisplayName": "Ejecutar flujo de trabajo extra",
                    "DisplayBefore": "ExtraWorkflowTime",
                    "Select": {
                        "Options": [
                            {
                                "Display": "Ejecutar meditación (opcional)",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": true
                                    }
                                }
                            },
                            {
                                "Display": "Saltar la atención plena del dispositivo",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": false
                                    },
                                    "Hide": [
                                        "ExtraWorkflowTime"
                                    ]
                                }
                            }
                        ],
                        
                    },
                    "Default": "Saltar la atención plena del dispositivo"
                }
            ]
        }
    }
}
```

Puede usar la misma notación / las mismas funciones en su [cabecera del runbook](#runbook-header).

#### ParameterList

Cada parámetro tiene su propia sección en `ParameterList`. [Modificadores](#modifiers) permiten cambiar el comportamiento de ese parámetro.

El resultado se verá así:

![Demo - después oculto](https://3832142177-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)

Elegir el flujo de trabajo adicional mostrará (desocultará) más parámetros:

![Demo - después desoculto](https://3832142177-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)

Esto muestra menos desorden en comparación con antes de aplicar la personalización. Al mismo tiempo, el usuario dispone de más información sobre las alternativas de "Extra Workflow". Además, ahora un usuario solo se preocupará por "Extra Workflow Time" si es relevante.

Cambiar la visibilidad de ese campo se realizó usando un `"Customization"` bloque dentro de una de las opciones de `"Select"` . Actualmente puede tener como máximo un bloque de este tipo `"Customization"` activo a la vez.

Como puede ver, el parámetro `$DeviceId` está completamente oculto. Esto se hace configurando el `"Hide": true` para este parámetro.

Los parámetros pueden tener un `DisplayName`. Ofrecimos un `DisplayName` amigable para el usuario `para reemplazar` $ExtraWorkflowTime [en la interfaz. Consulte otros](#modifiers) modificadores

Puede insertar parámetros "sin nombre" (faltando la `Nombre` declaración) como en la sección "Execute Extra Workflow", si quiere ofrecer elementos de la interfaz de usuario sin devolver directamente un valor. Esto normalmente solo se usa junto con `Selecciona`.

#### Selecciona

Usamos `Selecciona`, para mostrar una lista de `Opciones` en un desplegable. Cada opción puede `Mostrar` texto, o activar un `Personalización`, como establecer `Ocultar` o un `Predeterminado` valor en otros parámetros. En nuestro ejemplo, lo usamos para mostrar/ocultar (o no) `para reemplazar` y sobrescribir `$ExtraWorkflow`su valor.

`para reemplazar` por lo tanto solo se muestra cuando es relevante y el interruptor binario `$ExtraWorkflow` ahora se sustituye por alternativas con significado desde la perspectiva del usuario.

En caso de un `Selecciona` para un parámetro con nombre, cada opción debería tener un `"ParameterValue": "..."` para pasar al runbook. Puede colocar un `"ShowValue: false"` dentro del bloque `Selecciona` para mostrar solo el desplegable y no un campo para el valor resultante del parámetro.

Ejemplo de parámetro con nombre:

```json
{
    "Name": "ExtraWorkflow",
    "DefaultValue": true,
    "DisplayName": "Ejecutar flujo de trabajo extra",
    "DisplayBefore": "ExtraWorkflowTime",
    "Select": {
        "Options": [
            {
                "Display": "Ejecutar meditación (opcional)",
                "ParameterValue": true
            },
            {
                "Display": "Saltar la atención plena del dispositivo",
                "ParameterValue": false,
                "Customization": {
                    "Hide": [
                        "ExtraWorkflowTime"
                    ]
                }
            }
        ],
        "ShowValue": false
    }
}
```

El `Predeterminado` / `DefaultValue` declaración en el parámetro también especifica el estado inicial del desplegable. En caso de un parámetro sin nombre, use el `DisplayName` de la opción deseada; de lo contrario, indique un valor de retorno predeterminado, como "true" o "false" o alguna cadena.

#### Parámetros

Si solo tiene parámetros con nombre, puede usar el formato ligeramente más corto `Parámetros` en lugar de `ParameterList`.

Para un ejemplo, vea SelectSimple

#### SelectSimple

Si no se necesita toda la potencia de un `Selecciona` y solo quiere ofrecer una lista de valores posibles en un desplegable (sin aplicar personalización adicional), puede usar `SelectSimple`.

`SelectSimple` solo se puede usar para parámetros con nombre.

Ejemplo:

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "Parameters": {
                "DeviceId": {
                    "Hide": true
                }, 
                "ExtraWorkflow": {
                    "Name": "ExtraWorkflow",
                    "DisplayName": "Ejecutar flujo de trabajo extra",
                    "Default": false,
                    "SelectSimple": {
                        "Execute Meditation (optional)": true,
                        "Skip Device Mindfulness": false
                    }
                },
                "ExtraWorkflowTime": {
                    "DisplayName": "¿Cuánto tiempo meditar?"
                }
            }
        }
    }
}
```

La mayor diferencia (además de ser mucho más corto) con nuestro ejemplo anterior es que `para reemplazar` siempre es visible.

#### Modificadores

Cada parámetro puede tener uno o más de los siguientes modificadores:

* `"DisplayName": "texto"` - Mostrar "texto" como nombre del parámetro en la interfaz de usuario
* `"Hide": true / false` - Ocultar este parámetro
* `"Mandatory": true / false` - Requerir que este parámetro se complete
* `"ReadOnly": true / false` - Proteger este parámetro para que no se cambie respecto a su valor predeterminado
* `"DefaultValue": "..."` - Establecer un valor predeterminado para este parámetro. (También puede usar `Predeterminado` en su lugar.)
* `"GraphFilter": "startswith(DisplayName, 'LIC_')"` - ver [Filtrado de Graph](#graph-filtering)
* `"AllowEdit": true / false` - Proteger este parámetro de la edición manual. (combine esto con plantillas)

### Configuración

`Configuración` le permite almacenar datos de configuración, como nombres de cuentas de Azure Storage, en un lugar central, manteniéndolos al mismo tiempo separados de sus runbooks.

Puede acceder a valores individuales de un bloque de parámetros de un runbook usando `Use-RJInterface`.

Tomemos este bloque de parámetros de ejemplo de un 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
)
```

Portal intentará rellenar previamente cada parámetro con valores del almacén de datos central, si están presentes. Esto también funciona si el parámetro se ha ocultado en la interfaz de usuario.

Un posible JSON en el almacén de datos para este runbook sería:

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

El `Container` faltante simplemente no se rellenará previamente en la interfaz de usuario.

### Plantillas

`Plantillas` use referencias JSON para incorporar datos — por ejemplo, una larga lista de ubicaciones de oficinas — al usar una `Selecciona` declaración.

Esto permite mantener una personalización neutral/reutilizable/separada de los datos reales.

Tomemos el ejemplo de la incorporación de nuevos usuarios. Podría tener varias opciones dadas para departamentos o ubicaciones de oficina, donde asignar una ubicación de oficina también obliga a una determinada dirección, país, estado, etc.

El siguiente ejemplo de una personalización de runbook usa la `$ref` dentro del bloque `Runbooks` sección para referenciar/importar un subárbol desde la `Plantillas` sección. Preste atención a las palabras clave `$id`/`$values` . Tenga en cuenta que `$id`/`$values` deben definirse antes de referenciarlos usando `$ref`. Por eso `Plantillas` se define antes que `Runbooks` en este ejemplo.

En este ejemplo, le indicamos al portal que tome el subárbol con el `$id` llamado `LocationOptions` e incluya sus `$values`, reemplazando la declaración `$ref` . Así, el portal renderizará un `Selecciona` como se describe en la sección `Runbooks` pero incluirá las opciones reales de `Plantillas`.

Una plantilla puede contener cualquier declaración admitida en la ubicación de referencia. En este ejemplo, usamos una declaración `Personalización` para modificar otros parámetros como `StreetAddress`.

Así, podemos tener una personalización específica del runbook en `Runbooks` reutilizable en múltiples entornos, manteniendo los datos reales 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": "Ubicación de la oficina",
                    "DisplayAfter": "CompanyName",
                    "Select": {
                        "Options": {
                            "$ref": "LocationOptions"
                        }
                    }
                },
                {
                    "Name": "CompanyName",
                    "Select": {
                        "Options": {
                            "$ref": "CompanyOptions"
                        },
                        "AllowEdit": false
                    }
                }
            ],
            "ReadOnly": [
                "StreetAddress",
                "PostalCode",
                "City",
                "Country"
            ]
        }
    }
}
```

Esto creará la siguiente interfaz de usuario:

![Demo - ref-location](https://3832142177-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://3832142177-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 de Graph

Puede preparar [filtros ODATA de Graph](https://docs.microsoft.com/en-us/graph/query-parameters?context=graph%2Fapi%2F1.0\&view=graph-rest-1.0#filter-parameter) para usarlos en varios runbooks. Guárdelos en una sección llamada `GraphFilters`.

El siguiente ejemplo filtra un cierto prefijo en el `DisplayName` de un grupo, para mostrar solo grupos relacionados con licencias en un selector de grupos.

```json
"GraphFilters": {
    "LicenseGroup": "startswith(DisplayName, 'LIC_')" // también incluido en el código RJ como valor predeterminado
  }
```

Consulta [Filtrado de Graph](#graph-filtering) sobre cómo usar esto desde un runbook.
