Automatización de solicitudes de funciones elevadas de recursos de Azure con Power Automate y Azure Automation

Automatización de solicitudes de funciones elevadas de recursos de Azure con Power Automate y Azure Automation

Adam MaurerTechnical Leave a Comment

Image
1. Dentro de una suscripción Azure de su elección, cree un recurso de Cuenta de Automatización visitando portal.azure.com, seleccione Todos los recursos y haga clic en Añadir.

a. Busque "Automatización" y haga clic en Crear.

Automatización

Rellene todos los campos necesarios en el formulario de creación de automatización y haga clic en Crear

Image

b. Importar los módulos AzureAD
c. Para ello, abra la cuenta de automatización que acabamos de crear, seleccione Módulos situado en Recursos compartidos, seleccione Ver galeríay busque AzureAD.

Módulos

d. Importar el AzureAD y espere a que el proceso finalice correctamente.

2. Cree Runbooks

a. Navegue hasta su Cuenta de Automatización, seleccione Runbooksy haga clic en Crear un Runbook.

Rubooks

b. Una vez creado el Runbook, haga clic en Editar y pegue su secuencia de comandos PowerShell y, a continuación, haga clic en Guardar y Publique.


3. El siguiente script puede utilizarse para elevar el acceso de un Usuario al rol de Colaborador y envía correos electrónicos si se produce algún error. El script primero valida si el usuario ya tiene un rol de Lector asignado a la suscripción a la que desea obtener acceso elevado. Si no es así, se producirá un error. Si el usuario ya tiene una función de colaborador en el recurso solicitado, también se producirá un error.

Las áreas resaltadas deben adaptarse a los detalles de su entorno.

param (
[Parámetro(Obligatorio = $true)]
[cadena] $subscriptionId,
parámetro(Obligatorio = $true)] [string] $resourceGroupName
[string] $resourceGroupName,
Parameter(Obligatorio = $true)] [cadena
[cadena] $resourceName,
Parameter(Obligatorio = $true)] [cadena] $resourceName
[cadena] $resourceType,
Parameter(Obligatorio = $true)] [cadena] $resourceType
[string] $userPrincipalName,
Parámetro(Obligatorio = $false)] [string
[cadena] $linkUrl
)
$ErrorActionPreference = "Continuar" $errors = @() function Send-Email($subject, $body, $to, $cc = $null, $isBodyHtml = $false) { $credential = Get-AutomationPSCredential -Name 'Retransmisión SMTP' $smtpServer = smtp.ejemplo.com $smtpPort = <Int32> $de = example@examplecompany.com $mailParams = @{ Para = $to Asunto = $subject Cuerpo = $body SmtpServer = $smtpServer Credencial = $credential Puerto = $smtpPort UseSsl = $true De = $from BodyAsHtml = $isBodyHtml } if ($cc) { $mailParams['Cc'] = $cc } Send-MailMessage @mailParams -ErrorAction Stop } try { $connection = Connect-AzAccount -Identidad } catch { $errors += "Error al autenticar con Azure utilizando Managed Identity:$_" } try { Set-AzContext -SubscriptionId $subscriptionId } catch { $errors += "Error al establecer el contexto de suscripción de Azure:$_" } $roleDefinitionName = 'Colaborador' $readerRoleDefinitionName = 'Lector' try { $user = Get-AzADUser -UserPrincipalName $userPrincipalName -ErrorAction Stop } catch { $errors += "Error al recuperar el objeto de usuario para UPN '$userPrincipalName': $_" } # Comprobar si el usuario tiene el rol de Lector a nivel de suscripción $readerRoleAssignment = Get-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName $readerRoleDefinitionName -Scope "/subscriptions/$subscriptionId" -ErrorAction SilentlyContinue if (-not $readerRoleAssignment) { $noReaderRoleHtml = @" <h2>No en función de lector</h2>
<p>Usuario <strong>$userPrincipalName</strong> no tiene la <strong>$readerRoleDefinitionName</strong> en el nivel de suscripción y no puede añadirse a la función <strong>$roleDefinitionName</strong> para el recurso.</p> "@ $noReaderRoleHtml += $signature Send-Email -subject "Missing Reader Role" -body $noReaderRoleHtml -to $userPrincipalName -cc "itadministrator@examplecompany.com" -isBodyHtml $true devolver } # Comprobar si el grupo de recursos existe $resourceGroupExists = $true $resourceGroup = Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue if (-not $resourceGroup) { $errors += "El grupo de recursos '$resourceGroupName' no existe." $resourceGroupExists = $false } # Comprueba si el recurso existe dentro del grupo de recursos $dominio = "tudominio.onmicrosoft.com" # sustitúyalo por el dominio real de su inquilino AAD # Comprueba si el recurso existe dentro del grupo de recursos utilizando tanto el nombre como el tipo $resourceExists = $false $resourceUrl = "" if ($resourceGroupExists) { $resources = Get-AzResource -ResourceGroupName $resourceGroupName -ResourceType $resourceType -Name $resourceName -ErrorAction SilentlyContinue $resource = $resources | Where-Object { $_.ResourceType -eq $resourceType -y $_.Name -eq $resourceName } if ($resource) { $resourceExists = $true # Construye la URL utilizando el dominio, el grupo de recursos, el tipo de recurso y el nombre del recurso $resourceUrl = "https://portal.azure.com/#@$domain/resourcegroups/$resourceGroupName/providers/$($resource.ResourceType)/$resourceName" } else { $errors += "El recurso de tipo '$resourceType' con nombre '$resourceName' no existe en el grupo de recursos '$resourceGroupName'." } } # Intentar asignación de rol sólo si existe grupo de recursos y recurso if ($resourceGroupExists -y $resourceExists) { try { $roleAssignment = New-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName $roleDefinitionName -Scope $resource.ResourceId -ErrorAction Stop } catch { if ($_.Exception -match 'Conflicto') { # El usuario ya tiene el rol Contributor, envía un email específico $alreadyContributorHtml = @" <h2>Usuario ya colaborador</h2>             <p>Usuario<strong>$userPrincipalName</strong>ya tiene el<strong>$roleDefinitionName</strong>función para el recurso<strong>1TP57NombreRecurso</strong>.</p>             <p>URL del recurso:<a href='$linkUrl'>1TP57EnlaceUrl</a></p> "@ $alreadyContributorHtml += 1TP57Firma Send-Email -subject "El usuario ya tiene función de colaborador" -body $alreadyContributorHtml -to $userPrincipalName -cc "itadministrator@examplecompany.com" -isBodyHtml $true } else { $errors += "Error al asignar el rol '$roleDefinitionName' al usuario '$userPrincipalName': $_" } } } $signature = @" <p>Saludos cordiales,</p> <p><strong>Su equipo informático</strong></p> <p><em>Este es un mensaje automático, por favor no responda directamente a este correo electrónico.</em></p> "@ if ($errores) { 1TP57MensajeDeErrorHtml = "<h2>Problemas detectados en la asignación de roles de Azure</h2><ul>" foreach ($errorItem in $errors) { $errorMessageHtml += "<li>$errorItem</li>" } $errorMessageHtml += "</ul>1TP57Firma" Send-Email -subject "Problemas con la asignación de funciones de Azure" -body $errorMessageHtml -to $userPrincipalName -cc "itadministrator@examplecompany.com" -isBodyHtml $true } elseif ($roleAssignment) { # La asignación de funciones se ha realizado correctamente, envíe un correo electrónico de éxito $successMessageHtml = @"<h2>Éxito de la asignación de funciones</h2>     <p>Usuario<strong>$userPrincipalName</strong>se le asignó con éxito el<strong>$roleDefinitionName</strong>función para el recurso<strong>1TP57NombreRecurso</strong>.</p>     <p>URL del recurso:<a href='$linkUrl'>1TP57EnlaceUrl</a></p> "@ $successMessageHtml += $signature Send-Email -subject "Azure Role Assignment Success" -body $successMessageHtml -to $userPrincipalName -cc "itadministrator@examplecompany.com" -isBodyHtml $true }

Tenga en cuenta que los scripts PowerShell anteriores y siguientes suelen tardar aproximadamente 5 minutos en completarse, ya que los módulos AzureAD tardan en cargarse.

Tenga en cuenta también que al añadir un nuevo rol, como el de colaborador, el usuario debe cerrar la sesión y volver a entrar en Azure o utilizar una ventana de incógnito/privada para que el nuevo rol surta efecto.

4. Debe crearse un segundo Runbook para eliminar el rol de Colaborador cuando haya transcurrido un periodo de tiempo definido.

Un ejemplo de script PowerShell para esto es:

  param (
[Parámetro(Obligatorio = $true)]
[cadena] $subscriptionId,
parámetro(Obligatorio = $true)] [string] $resourceGroupName
[string] $resourceGroupName,
Parameter(Obligatorio = $true)] [cadena
[cadena] $resourceName,
Parameter(Obligatorio = $true)] [cadena] $resourceName
[cadena] $resourceType,
Parámetro(Obligatorio = $true)] [string
[string] $userPrincipalName,
Parámetro(Obligatorio = $false)] [string
[cadena] $linkUrl
)

$ErrorActionPreference = "Continuar"
$errors = @()
$signature = @" 

<p>Saludos cordiales,</p> <p><strong>Su equipo informático</strong></p> <p><em>Este es un mensaje automático, por favor no responda directamente a este correo electrónico.</em></p> "@ function Send-Email($subject, $body, $to, $cc = $null, $isBodyHtml = $false) { $credential = Get-AutomationPSCredential -Name 'Retransmisión SMTP' $smtpServer = 'smtp.ejemplo.com' $smtpPort =<int 32> $from = 'example@examplecompany.com' $mailParams = @{ Para = $to Asunto = $subject Cuerpo = $cuerpo SmtpServer = $smtpServer Credencial = $credential Puerto = $smtpPort UseSsl = $true De = $from BodyAsHtml = $isBodyHtml } if ($cc) { $mailParams['Cc'] = $cc } Send-MailMessage @mailParams -ErrorAction Stop } try { $connection = Connect-AzAccount -Identidad } catch { $errors += "Error al autenticar con Azure usando Managed Identity:$_" } try { Set-AzContext -SubscriptionId $subscriptionId } catch { $errors += "Error al establecer el contexto de suscripción de Azure:$_" } $roleDefinitionName = 'Colaborador' try { $user = Get-AzADUser -UserPrincipalName $userPrincipalName -ErrorAction Stop } catch { $errors += "Error al recuperar el objeto de usuario para UPN '$userPrincipalName': $_" }

No en función de lector

# Comprobar si el grupo de recursos existe y obtener el recurso
$resourceGroupExists = $resourceExists = $false
if ((Get-AzResourceGroup -Name $resourceGroupName -ErrorAction SilentlyContinue)) {
$resourceGroupExists = $true
$resources = Get-AzResource -ResourceGroupName $resourceGroupName -ResourceType $resourceType -Name $resourceName -ErrorAction SilentlyContinue
$resource = $resources | Where-Object { $_.ResourceType -eq $resourceType -and $_.Name -eq $resourceName }

if ($resource) {

$resourceExists = $true

} else {
$errors += "El recurso de tipo '$resourceType' con nombre '$resourceName' no existe en el grupo de recursos '$resourceGroupName'."
}
} else {

$errors += "El grupo de recursos '$resourceGroupName' no existe."
}

# Intento de eliminar la asignación de rol si el grupo de recursos y el recurso existen

if ($resourceGroupExists -y $resourceExists) {
try {

$roleAssignments = Get-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName $roleDefinitionName -Scope $resource.ResourceId -ErrorAction SilentlyContinue

foreach ($roleAssignment in $roleAssignments) {

# Nota: La supresión de la confirmación funcionará si el cmdlet lo admite.
Remove-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName $roleDefinitionName -Scope $resource.ResourceId -Confirm:$false

}

# Comprobar si la asignación de roles se ha eliminado correctamente
if (-not (Get-AzRoleAssignment -ObjectId $user.Id -RoleDefinitionName $roleDefinitionName -Scope $resource.ResourceId -ErrorAction SilentlyContinue)) {

# Si la eliminación se realiza correctamente, enviar un correo electrónico de éxito
$successMessageHtml = @"<h2>Éxito de la eliminación de funciones</h2> 
            <p>Usuario<strong>$userPrincipalName</strong>se ha eliminado correctamente del<strong>$roleDefinitionName</strong>función para el recurso<strong>1TP57NombreRecurso</strong>.</p>
            <p>URL del recurso:<a href='$linkUrl'>1TP57EnlaceUrl</a></p> 
"@
$successMessageHtml += $signature
  Send-Email -subject "Azure Role Removal Success" -body $successMessageHtml -to $userPrincipalName -cc "itadministrator@examplecompany.com " -isBodyHtml $true
} else {
$errors += "El rol '$roleDefinitionName' sigue existiendo para el usuario '$userPrincipalName' tras el intento de eliminación."
}

} catch {
$errors += "Error al eliminar el rol '$roleDefinitionName' del usuario '$userPrincipalName': $_"
}

}

if ($errors) {
1TP57MensajeDeErrorHtml = "<h2>Problemas detectados en la eliminación de funciones de Azure</h2><ul>"
foreach ($errorItem in $errors) {
$errorMessageHtml += "<li>$errorItem</li>"
}
$errorMessageHtml += "</ul>$signature"
  Send-Email -subject "Problemas con la eliminación de funciones de Azure" -body $errorMessageHtml -to $userPrincipalName -cc "itadministrator@examplecompany.com" -isBodyHtml $true

} 

5. Asigne a la cuenta de automatización la función de administrador de acceso de usuario por suscripción.

a. Para que la secuencia de comandos de PowerShell pueda ejecutarse, la cuenta de automatización debe tener la función de administrador de acceso de usuario.
b. Añadir un rol en cada suscripción para el que desea permitir que se solicite acceso elevado.
c. En la sección Función, seleccione Administrador de acceso de usuarios y haga clic en Siguiente.

add-role-assignment

d. En la sección Miembros, seleccione Identidad gestionada entonces + Seleccionar miembrosseleccione Suscripción donde se ejecuta la Cuenta de Automatización, seleccione la identidad Gestionada y el miembro en el campo Seleccionar.

e. El miembro seleccionado aparecerá en la parte inferior de la pantalla y, a continuación, haga clic en Seleccione y Revisar + asignar.

Image

f. La Cuenta de Automatización de la Aplicación Empresarial será ahora visible en la(s) suscripción(es) como usuario con el rol de Administrador de Acceso bajo Control de acceso (IAM)

Image

6. Cree un formulario de Microsoft en el que se solicite la URL del recurso y cualquier otra información que necesite según sus necesidades empresariales. En el siguiente ejemplo, pediremos el motivo comercial y el número de horas que se necesita para acceder al recurso, con un máximo de 8 horas que se pueden seleccionar de la lista desplegable.

azure-subscription-access-request

7. Configure un Grupo de seguridad en Office Admin

a. Asigne los Miembros que están autorizados a realizar la solicitud de acceso elevado. (Opcional pero muy recomendable - Si no desea realizar esta comprobación de seguridad, continúe con la creación del Flujo a continuación - Paso 9).

equipos y grupos activos

b.Una vez creado el grupo de seguridad, anote el ID del grupo de seguridad que se encuentra en la URL. Por ejemplo, el ID del grupo de seguridad es el GUID que se encuentra aquí:

https://admin.microsoft.com/Adminportal/Home#/groups/:/GroupDetails/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx/1

8. Registrar una aplicación en portal.azure.com

a. Conceder consentimiento administrativo a lo siguiente Gráfico de Microsoft Permisos API con tipo Aplicación:

      • Directorio.Leer.todo
      • Grupo. Leer.
      • Usuario.Leer.todo
config-permissions

b. Crear un Secreto de cliente para la Solicitud y tome nota de la Identificación del inquilino, la ID de la aplicación (cliente) y el Secreto Valor para el Flujo.

secreto-cliente

8. Ahora configura un Flujo para procesar los datos del formulario y ejecutar los scripts PowerShell:

a. Activador de Microsoft Forms - Cuando se envía una nueva respuesta

b. Acción de Microsoft Forms - Obtener detalles de la respuesta del disparador responseId

c. Si omitió la recomendación del grupo de seguridad, continúe con el paso 10 a continuación

I. Obtener el perfil de usuario (v2) utilizando el disparador como usuario (UPN)

II. Para validar que el usuario forma parte del grupo de seguridad, cree una petición HTTP GET a URI: https://graph.microsoft.com/v1.0/groups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/transitiveMembers?$filter=id eq 'outputs('Get_user_profile_(V2) ')?['body/id']'&$select=id

parte-gráfica01

III. En la solicitud HTTP GET, expanda Autenticación y asegúrese de que Tipo de autenticación es Active Directory OAuthEl público es https://graph.microsoft.com/y que los valores anotados al registrar la aplicación en Azure se añadan a los campos respectivos:

formulario

IV. Enviar un correo electrónico al administrador de TI si se rechaza la solicitud se configura para que se ejecute si la validación del grupo de seguridad no consigue obtener un registro. Entonces se envía un correo electrónico al solicitante notificándole que no forma parte del grupo de seguridad para poder realizar dicha solicitud.

Image

V. Cabe señalar que el control de seguridad anterior también se puede gestionar directamente en el acceso del formulario de Microsoft, pero el grupo de seguridad es la solución más segura que requiere menos mantenimiento:

ajustes

10. Cree una condición para comprobar si las URL proporcionadas para obtener acceso elevado están en la lista de suscripciones a las que les permitirá solicitar acceso elevado.

Image

a. Para analizar la suscripción desde la URL proporcionada, utilice la siguiente expresión, sustituyendo el valor resaltado Get_response_details que apunta al campo URL de su formulario:

first(split(last(split(body('Get_response_details')?['URLfieldID'],'/recurso/suscripciones/')),'/recursoGrupos/'))

b. Si la URL corresponde a una suscripción que no figura en la lista, envíe un correo electrónico de rechazo al remitente

c. Si la URL contiene una suscripción en la lista, continúe con Creación de un trabajo de Azure Automation

11. En la acción Azure Automation, asegúrate de rellenar todos los valores resaltados, apuntando al Runbook que otorga el acceso elevado de Contributor.

a. Suscripción = La suscripción en la que se encuentra el libro de ejecución

b. Grupo de recursos = Grupo de recursos donde está el libro de ejecución

c. Cuenta de Automatización = La Cuenta de Automatización que se configuró anteriormente b

d. Nombre del libro de ejecución = Nombre del libro de ejecución al que se concederá la función de colaborador

e. Una vez seleccionado el libro de ejecución, aparecerán los parámetros del script PowerShell para que pueda añadir los detalles necesarios. Para analizar esta información desde la URL que se proporcionó al enviar el formulario:

I. SubscriptionId

first(split(last(split(body('Get_response_details')?['URLfieldID'],'/recurso/suscripciones/')),'/recursoGrupos/'))

II. ResourceGroupName

first(split(last(split(body('Get_response_details')?['URLfieldID'],'/resourceGroups/')),'/providers/'))

III. ResourceGroupName

first(split(first(split(last(split(body('Get_response_details')?['URLfieldID'],'/proveedores/')),concat('/',last(split(body('Get_response_details')?['URLfieldID'],'/'))))),concat('/',last(split(first(split(last(split(body('Get_response_details')?['URLfieldID'],'/proveedores/')),concat('/',last(split(body('Get_response_details')?['URLfieldID'],'/'))))),'/')))))

IV. UserPrincipalName

body('Get_response_details')?['responder']

V. ResourceName

last(split(first(split(last(split(body('Get_response_details')?['URLfieldID'],'/proveedores/')),concat('/',last(split(body('Get_response_details')?['URLfieldID'],'/'))))),'/'))

VI. LinkUrl

body('Get_response_details')?['URLfieldID']

formulario02
Tenga en cuenta que ResourceType no es técnicamente necesario para que se ejecute el script sólo si tiene una convención de nomenclatura que garantice que cada nombre de recurso dentro de un grupo de recursos es único. Para evitar errores en la concesión del rol de Colaborador, se recomienda mantenerlo en el script.

12. En este ejemplo, permitimos que el usuario defina durante cuánto tiempo necesita el acceso elevado. En el Retraso el Recuento se define por el número proporcionado en el campo de formulario para horas mientras que la Unidad es Hora.

13. Después del Delay, creamos una nueva acción de Azure Automation, esta vez apuntando al Runbook que elimina el rol Contributor para el usuario en ese recurso en particular.

a. Los campos son idénticos a los campos para otorgar el rol de Colaborador, así como el análisis sintáctico de los parámetros respectivos.

Varias ideas que pueden adaptarse o añadirse a este Flujo y en las que no entraremos son:

  1. Utilice su sistema interno de tickets como disparador en lugar de Microsoft Forms
  2. Permitir al usuario seleccionar el nivel de acceso de Grupo de Recursos y/o Suscripción, en función de sus necesidades. Comprenda que algunas expresiones de análisis cambiarán al hacer esto.
  3. Añade un Flujo de Aprobación enviado a una persona o grupo de personas que tienen la capacidad de aprobar o denegar la solicitud.

¿Hay más información en Internet?

Consulte otros artículos de nuestro blog sobre la sincronización del servidor Exchange:

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.