# Personalización de runbooks

## Información general

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

* Alojar parámetros y plantillas específicos del cliente/Tenant
* Ofrecer elementos de UI como selectores de usuario o selecciones desplegables
* Presentar explicaciones de parámetros legibles para humanos
* Ocultar elementos de UI innecesarios

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

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

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

### Formato

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

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

Además (con la menor prioridad)

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

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

## Bloque Param del Runbook

RealmJoin Portal analiza el bloque param de PowerShell de un runbook para determinar qué campos de entrada representar. Cuando sea posible, también validará las entradas de acuerdo con el tipo .NET dado para una variable.

Actualmente se entienden los siguientes tipos de datos:

* `[bool]`, `[boolean]` - presentará un interruptor 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. RealmJoin Portal, en particular, entenderá si especifica `[Parameter(Mandatory = $true)]` para indicar un parámetro obligatorio y exigir que estos parámetros se completen.

Cuando sea posible, RealmJoin Portal también leerá y presentará en la UI los valores predeterminados dados.

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

### Personalización de parámetros

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

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

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

```powershell
param(
    [ValidateScript( { Use-RJInterface -DisplayName "Assign device to this user (optional)" -Type Graph -Entity User } )]
    [string] $AssignedUserId = ""
)
```

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

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

`-DisplayName` le permite pasar a RealmJoin Portal una indicación/descripción legible para humanos de este parámetro.

#### Recursos Graph

En el ejemplo anterior, la fuente de información es MS Graph, como se describe mediante `-Type Graph`. Para MS Graph, use `-Entity` para especificar qué tipo de recurso espera. 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 encontrar fácilmente el recurso requerido.

![Ejemplo de selector](/files/7506db651a8786f2e44b8a08814b5a6cf31d732f)

Actualmente, no es posible la selección múltiple 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 listará solo grupos de Entra ID que empiecen con "LIC\_".

```powershell
param(
    [Parameter(Mandatory = $true)]
    [ValidateScript( { Use-RJInterface -Type Graph -Entity Group -Filter "startswith(DisplayName, 'LIC_')" -DisplayName "License group" } )]
    [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 configuración adicional.

![filtro ODATA](/files/71860a6593755dc4e0baf0c416860ba260ad0fd7)

## Encabezado 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 quitar una licencia a un usuario mediante pertenencia a un grupo.

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

  .PARAMETER DefaultGroups
  Lista de grupos separados por comas para 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": "Assign or Remove License",
                "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 algo más de detalle, ya que esto se mostrará dentro del 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 / representa. Utilice este espacio para anotar qué permisos y requisitos existen para su runbook.

`.EXAMPLE` - No se analiza / representa. Puede contener un ejemplo de una personalización basada en JSON para usar en el almacén de datos de RealmJoin de su Tenant. Estos pueden ser ejemplos de cómo crear plantillas, p. ej., para diferentes 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 "Personalizaciones de runbooks", 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 el frontend mostrado al usuario.

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

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

  .DESCRIPTION
  Demostrar la 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 al 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 el frontend:

![Demostración - antes](/files/a152cea1c1573e4e73c2b56f219bb12b81bf19f7)

Reflexiones:

* 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é con qué dispositivo estoy trabajando.
* ¿Qué pasa si activo o desactivo el "Flujo de trabajo extra"? ¿Necesito pensar en el "Tiempo del flujo de trabajo extra" si desactivo el "Flujo de trabajo extra"?

Mejoremos eso. El siguiente ejemplo de JSON en el almacén de datos central modificará la UI del runbook.

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "ParameterList": [
                {
                    "Name": "DeviceId",
                    "Hide": true
                }, 
                {
                    "Name": "ExtraWorkflow",
                    "Hide": true
                },
                {
                    "Name": "ExtraWorkflowTime",
                    "DisplayName": "How long to meditate?",
                },
                {
                    "DisplayName": "Execute Extra Workflow",
                    "DisplayBefore": "ExtraWorkflowTime",
                    "Select": {
                        "Options": [
                            {
                                "Display": "Execute Meditation (optional)",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": true
                                    }
                                }
                            },
                            {
                                "Display": "Skip Device Mindfulness",
                                "Customization": {
                                    "Default": {
                                        "ExtraWorkflow": false
                                    },
                                    "Hide": [
                                        "ExtraWorkflowTime"
                                    ]
                                }
                            }
                        ],
                        
                    },
                    "Default": "Skip Device Mindfulness"
                }
            ]
        }
    }
}
```

Puede usar la misma notación / funcionalidades en el [encabezado 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í:

![Demostración - después oculto](/files/d53c9da577ab8b250d952d5785fc7612552ba3f5)

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

![Demostración - después mostrado](/files/50e8d01f4c2d67ce7c4644a2e9fff8d6e3286d5d)

Esto muestra menos desorden en comparación con antes de aplicar la personalización. Al mismo tiempo, hay más información disponible para el usuario sobre las alternativas de "Flujo de trabajo extra". Además, ahora un usuario solo se preocupará por el "Tiempo del flujo de trabajo extra" si es relevante.

Cambiar la visibilidad de ese campo se hizo usando un bloque `"Customization"` dentro de una de las opciones `"Select"` Actualmente puede tener como máximo uno de esos bloques `"Customization"` activo a la vez.

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

Los parámetros pueden tener un `DisplayName`. Ofrecimos un `DisplayName` amigable para humanos para reemplazar `$ExtraWorkflowTime` en la UI. Vea otros [modificadores](#modifiers) para más información.

Puede insertar parámetros "sin nombre" (sin la instrucción `Nombre` ) como la sección "Execute Extra Workflow", si quiere ofrecer elementos de UI sin devolver directamente un valor. Normalmente esto solo se usa junto con `Seleccione`.

#### Seleccione

Usamos `Seleccione`, para mostrar una lista de `Opciones` en un desplegable. Cada opción puede `Mostrar` texto, o activar una `Personalización`, como establecer `Hide` o un valor `Default` en otros parámetros. En nuestro ejemplo, lo usamos para ocultar/mostrar `$ExtraWorkflowTime` y sobrescribir el valor de `$ExtraWorkflow`.

`$ExtraWorkflowTime` por lo tanto solo se muestra cuando es relevante y el interruptor binario `$ExtraWorkflow` ahora se reemplaza con alternativas significativas desde la perspectiva del usuario.

En el caso de un `Seleccione` para un parámetro con nombre, cada opción debe tener un `"ParameterValue": "..."` para pasar al runbook. Puede colocar un `"ShowValue: false"` dentro del bloque `Seleccione` 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": "Execute Extra Workflow",
    "DisplayBefore": "ExtraWorkflowTime",
    "Select": {
        "Options": [
            {
                "Display": "Execute Meditation (optional)",
                "ParameterValue": true
            },
            {
                "Display": "Skip Device Mindfulness",
                "ParameterValue": false,
                "Customization": {
                    "Hide": [
                        "ExtraWorkflowTime"
                    ]
                }
            }
        ],
        "ShowValue": false
    }
}
```

El `Default` / `La instrucción` DefaultValue `DisplayName` en el parámetro también especifica el estado inicial del desplegable. En el caso de un parámetro sin nombre, use el

#### Parámetros

de la opción deseada; de lo contrario dé 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 `ParameterList`.

en lugar de

#### Para un ejemplo vea SelectSimple

SelectSimple `Seleccione` Si no se necesita toda la potencia de un `Para un ejemplo vea SelectSimple`.

`Para un ejemplo vea SelectSimple` y solo quiere ofrecer una lista de posibles valores en un desplegable (sin aplicar personalización adicional), puede usar

Ejemplo:

```json
{
    "Runbooks": {
        "rjgit-device_demo-runbook-customizing": {
            "Parameters": {
                solo se puede usar para parámetros con nombre.
                    "Hide": true
                }, 
                "DeviceId": {
                    "Name": "ExtraWorkflow",
                    "DisplayName": "Execute Extra Workflow",
                    "ExtraWorkflow": {
                    "SelectSimple": {
                        "Default": false,
                        "Execute Meditation (optional)": true,
                    }
                },
                "Skip Device Mindfulness": false
                    "ExtraWorkflowTime": {
                }
            }
        }
    }
}
```

"DisplayName": "How long to meditate?" `$ExtraWorkflowTime` La mayor diferencia (además de ser mucho más corto) con respecto a nuestro ejemplo anterior es que

#### Modificadores

siempre está visible.

* `Cada parámetro puede tener uno o más de los siguientes modificadores:` "DisplayName": "text"
* `- Mostrar "text" como nombre del parámetro en la UI` "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 de su valor predeterminado` "DefaultValue": "..." `Default` - Establecer un valor predeterminado para este parámetro. (También puede usar
* `en su lugar.)` "GraphFilter": "startswith(DisplayName, 'LIC\_')" [Filtrado de Graph](#graph-filtering)
* `- vea` "AllowEdit": true / false

### Configuración

`Configuración` - Proteger este parámetro contra la edición manual. (combine esto con plantillas)

le permite almacenar datos de configuración como nombres de Azure Storage Account en un lugar central, manteniéndolos separados de sus runbooks. `Use-RJInterface`.

Puede acceder a valores individuales desde el bloque param de un runbook usando

```powershell
param(
    Tomemos este ejemplo de bloque param de un runbook:
    [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

El 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 ha sido ocultado en la UI.

```json
{
    "Settings": {
        Un posible JSON en el almacén de datos para este runbook sería:
            "CaPoliciesExport": {
            "ResourceGroup": "rj-runbooks-01",
                "StorageAccount": {
                "Name": "rjrbexports01",
                "Location": "West Europe",
            }
        }
    }
}
```

"Sku": "Standard\_LRS" `El elemento faltante` Container

### Plantillas

`Plantillas` simplemente no se rellenará previamente en la UI. `Seleccione` use referencias JSON para incorporar datos, por ejemplo una lista extensa de ubicaciones de Office, al usar una instrucción

.

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

Tomemos el ejemplo de la incorporación de nuevos usuarios. Puede tener múltiples opciones dadas para departamentos o ubicaciones de Office, donde asignar una ubicación de Office también exige una determinada dirección, país, estado, etc. `El siguiente ejemplo de una personalización de runbook usa la sección` dentro del bloque `Runbooks` $ref `Plantillas` para referenciar/importar un subárbol de la sección `. Preste atención a las palabras clave`/`$id` $values `. Preste atención a las palabras clave`/`$id` . Tenga en cuenta que `El siguiente ejemplo de una personalización de runbook usa la sección`deben definirse antes de referenciarlos usando `Plantillas` . Por eso `Runbooks` se define antes de

en este ejemplo. `. Preste atención a las palabras clave` En este ejemplo le decimos al portal que tome el subárbol con el `llamado` LocationOptions `$id`e incluya sus `El siguiente ejemplo de una personalización de runbook usa la sección` , reemplazando la instrucción `Seleccione` . Así, el portal representará un `Runbooks` como se describe en la sección `Plantillas`.

pero incluirá las opciones reales de `Personalización` Una plantilla puede contener cualquier instrucción que sea compatible en la ubicación que la referencia. En este ejemplo, usamos una instrucción `StreetAddress`.

para modificar otros parámetros como `Runbooks` Así, podemos tener una personalización específica del runbook en

```json
{
    "Templates": {
        "Options": [
            {
                reutilizable en varios entornos, mientras mantenemos los datos reales separados.
                "$id": "LocationOptions",
                    {
                        "$values": [
                        "Customization": {
                            "Default": {
                                "Display": "DE-OF",
                                "StreetAddress": "Kaiserstraße 39",
                                "PostalCode": "63065",
                                "City": "Offenbach",
                            }
                        }
                    },
                    {
                        "Country": "Germany"
                        "Customization": {
                            "Default": {
                                "Display": "DE-DEG",
                                "StreetAddress": "Lateinschulgassse 24-26",
                                "PostalCode": "94469",
                                "City": "Offenbach",
                            }
                        }
                    },
                    {
                        "City": "Deggendorf",
                        "Customization": {
                            "Default": {
                                "Display": "DE-HH",
                                "StreetAddress": "Hans-Henny-Jahnn-Weg 53",
                                "PostalCode": "22085",
                                "City": "Offenbach",
                            }
                        }
                    },
                    {
                        "City": "Hamburg",
                        "Customization": {
                            "Default": {
                                "Display": "FI-HS",
                                "StreetAddress": "Somewhere 42",
                                "PostalCode": "12345",
                                "City": "Helsinki",
                            }
                        }
                    }
                ]
            },
            {
                "Country": "Finland"
                "$id": "LocationOptions",
                    {
                        "$id": "CompanyOptions",
                        "Id": "gkg",
                        "Display": "glueckkanja",
                    },
                    {
                        "Value": "glueckkanja AG"
                        "Id": "pp",
                        "Display": "PRIMEPULSE",
                    }
                ]
            }
        ]
    },
    "Runbooks": {
        "Value": "PRIMEPULSE SE"
            "ParameterList": [
                {
                    "rjgit-org_general_add-user": {
                    "DisplayName": "Office Location",
                    "Select": {
                        "DisplayAfter": "CompanyName",
                            "Options": {
                        }
                    }
                },
                {
                    "$ref": "LocationOptions"
                    "Select": {
                        "DisplayAfter": "CompanyName",
                            "Name": "CompanyName",
                        },
                        "$ref": "CompanyOptions"
                    }
                }
            ],
            "AllowEdit": false
                "ReadOnly": [
                "StreetAddress",
                "PostalCode",
                "City",
            ]
        }
    }
}
```

"Country"

![Esto creará la siguiente UI:](/files/8fd5633b5691ba44c5de017dc084f34e0297d6bb)

![Demostración - ref-location](/files/ed7d68520e7e34ea8adb01165b6f8e78a690e080)

### Demostración - ref-address

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

GraphFilters `DisplayName` El siguiente ejemplo filtra por un determinado prefijo en el

```json
de un grupo, para mostrar solo grupos relacionados con licencias en un selector de grupos.
    "GraphFilters": {
  }
```

Vea ["LicenseGroup": "startswith(DisplayName, 'LIC\_')" // también incluido en el código RJ de forma predeterminada](#graph-filtering) Filtrado de Graph sobre cómo usar esto desde un 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/es/automatizacion/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.
