Kit de herramientas PowerShell para reconocimiento y pruebas de seguridad en Azure y Microsoft 365.
- 🚀 Inicio Rápido
- 🔐 Autenticación
- ⚡ Cheatsheet de Auditoría Rápida
- 📚 Reconocimiento Interno (Azure/M365)
- 🌐 Reconocimiento Externo (O365)
- 📝 Licencia
Importar el módulo:
Import-Module .\AzRA.psd1Obtener comandos disponibles:
Get-Command -Module AzRAUsar ayuda para cualquier función:
Get-Help Get-AzRA-Subscriptions -FullListar todas las suscripciones de Azure:
$token = "eyJ0eXAiOiJKV1QiLCJhbGc..." # Tu token de Azure Management
Get-AzRA-Subscriptions -AccessToken $tokenListar todos los usuarios:
$graphToken = "eyJ0eXAiOiJKV1QiLCJhbGc..." # Tu token de Microsoft Graph
Get-AzRA-Users -AccessToken $graphTokenObtener recursos de una suscripción:
Get-AzRA-ResourcesBySubscriptionID -AccessToken $token -SubscriptionID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"Obtener grupos y roles de un usuario:
Get-AzRA-RolesGroupsByEmail -AccessToken $graphToken -Email "usuario@dominio.com"Necesitas tokens de acceso válidos para las respectivas APIs:
API de Azure Management: https://management.azure.com/
API de Microsoft Graph: https://graph.microsoft.com/
Usa herramientas como az account get-access-token o adquiere tokens mediante el método que quieras/puedas.
# Importar módulo
Import-Module .\AzRA.psd1
# Obtener token de Azure Management (usando Azure CLI)
$azToken = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Obtener token de Microsoft Graph
$graphToken = (az account get-access-token --resource https://graph.microsoft.com | ConvertFrom-Json).accessTokenSecuencia de comandos para cubrir todos los vectores en una auditoría completa de Azure.
Definir $CLIENTPATH antes de empezar:
$CLIENTPATH = "C:\Audits\Cliente_YYYYMMDD"Connect-AzAccount
Get-AzRA-DeploymentParameterSecrets -OutputPath $CLIENTPATH -DumpRawRequiere:
Connect-AzAccount(credenciales interactivas o service principal).
az login
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
Get-AzRA-AutomationRunbooks -AccessToken $token -OutputPath $CLIENTPATH
Get-AzRA-LogicApps -AccessToken $token -OutputPath $CLIENTPATH -ScanSecrets -IncludeVersions
Get-AzRA-StorageAccounts -AccessToken $token -OutputPath $CLIENTPATH -ScanSecrets
Get-AzRA-KeyVaults -AccessToken $token -OutputPath $CLIENTPATH
Get-AzRA-VirtualMachines -AccessToken $token -OutputPath $CLIENTPATH
Get-AzRA-ContainerRegistries -AccessToken $token -OutputPath $CLIENTPATH -ScanRepositories
Get-AzRA-FunctionApps -AccessToken $token -OutputPath $CLIENTPATH -ScanSecrets -IncludeSlots
Get-AzRA-APIManagement -AccessToken $token -OutputPath $CLIENTPATH -ScanSecrets
Get-AzRA-CosmosDB -AccessToken $token -OutputPath $CLIENTPATH -ScanSecrets
Get-AzRA-EventHubs -AccessToken $token -OutputPath $CLIENTPATH -ScanSecrets
Get-AzRA-ManagedIdentities -AccessToken $token -OutputPath $CLIENTPATH -IncludeSystemAssigned
Get-AzRA-CognitiveServices -AccessToken $token -OutputPath $CLIENTPATH -ScanSecretsRequiere: token ARM (
https://management.azure.com).
$devopsToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 | ConvertFrom-Json).accessToken
Get-AzRA-AzureDevOps -AccessToken $devopsToken -OutputPath $CLIENTPATH -ScanSecretsRequiere: token Azure DevOps (
499b84ac-1321-427f-aa17-267ca6975798) o PAT con scopesvso.project,vso.serviceendpoint,vso.variablegroups_read,vso.code,vso.agentpools.
$graphToken = (az account get-access-token --resource https://graph.microsoft.com | ConvertFrom-Json).accessToken
Get-AzRA-EntraID -GraphToken $graphToken -IncludeMFAReport -IncludeApps -IncludeConditionalAccess -OutputPath $CLIENTPATHRequiere: token Graph (
https://graph.microsoft.com) conDirectory.Read.All,Reports.Read.All,Application.Read.AllyPolicy.Read.All.
Request-AzRA-Nonce- Solicitar nonce de Azure AD para un tenant
Get-AzRA-Subscriptions- Listar todas las suscripciones accesiblesGet-AzRA-ResourcesBySubscriptionID- Listar recursos de una suscripciónGet-AzRA-RoleAssignment- Obtener permisos para un recurso
Get-AzRA-Users- Listar todos los usuarios (con paginación)Get-AzRA-EnterpriseApplications- Listar registros de aplicaciones (con paginación)Get-AzRA-AppRoleAssignment- Obtener asignaciones de roles de una aplicaciónGet-AzRA-RolesGroupsByEmail- Obtener membresías de grupos y roles de usuario (con paginación)Invoke-AzRA-APIRequest- Hacer peticiones personalizadas a cualquier endpoint de Azure o Microsoft Graph
Get-AzRA-DeploymentParameterSecrets- Auditar el historial de deployments buscando credenciales en texto claro
Get-AzRA-AutomationRunbooks- Descargar y escanear Runbooks de Azure Automation buscando credenciales hardcodeadas
Get-AzRA-LogicApps- Enumerar Logic Apps y escanear definiciones, parámetros y acciones HTTP en busca de secretos y superficie de ataque
Get-AzRA-StorageAccounts- Enumerar Storage Accounts y auditar misconfiguraciones de seguridad, contenedores públicos y claves de acceso
Get-AzRA-KeyVaults- Enumerar Key Vaults y auditar misconfiguraciones de seguridad, configuración de recuperación, acceso de red y políticas de autorización
Get-AzRA-VirtualMachines- Enumerar Virtual Machines y SQL VMs auditando exposición de red, extensiones peligrosas, cifrado de disco y configuración de seguridad
Get-AzRA-EntraID- Auditar la configuración de seguridad del tenant de Entra ID: roles privilegiados, MFA, aplicaciones registradas, Conditional Access y políticas de directorio
Get-AzRA-ContainerRegistries- Enumerar Azure Container Registries, auditar misconfiguraciones de seguridad, obtener tokens ACR de data plane, enumerar repositorios/tags/tamaños e interactuar con imágenes para docker pull
Get-AzRA-FunctionApps- Enumerar Function Apps y App Services, auditar misconfiguraciones de seguridad y extraer app settings en texto claro (connection strings, API keys, client secrets)
Get-AzRA-APIManagement- Enumerar servicios de API Management, auditar configuración de red y protocolos, y extraer subscription keys, named value secrets y credenciales de backends
Get-AzRA-CosmosDB- Enumerar cuentas de Cosmos DB, auditar acceso de red y cifrado, y extraer master keys (acceso total de lectura/escritura a todos los datos)
Get-AzRA-EventHubs- Enumerar namespaces de Event Hubs, auditar firewall y configuración de TLS, y extraer SAS keys por regla de autorización incluyendo las de tipo Manage
Get-AzRA-ManagedIdentities- Enumerar User-Assigned Managed Identities, resolver sus role assignments y mapear rutas de escalada de privilegios (Owner/Contributor/UserAccessAdministrator)
Get-AzRA-CognitiveServices- Enumerar cuentas de Azure Cognitive Services (OpenAI, Speech, Language, Document Intelligence, Computer Vision, etc.) y servicios de Azure AI Search, auditar acceso de red y configuración de seguridad, y extraer API keys
Get-AzRA-AzureDevOps- Enumerar organizaciones, proyectos, service connections (con credenciales SPN), variable groups (secretos + Key Vault linked), agent pools self-hosted y repositorios en Azure DevOps
Solicitar un nonce de Azure AD para un tenant específico.
# Usando dominio
$nonce = Request-AzRA-Nonce -TenantID 'contoso.onmicrosoft.com'
Write-Output "Nonce obtenido: $nonce"
# Usando GUID del tenant
$nonce = Request-AzRA-Nonce -TenantID '12345678-1234-1234-1234-123456789abc'Listar todas las suscripciones de Azure accesibles.
# Obtener todas las suscripciones
$subs = Get-AzRA-Subscriptions -AccessToken $azToken
# Mostrar información básica
$subs | Select-Object subscriptionId, displayName, state
# Filtrar solo suscripciones activas
$subs | Where-Object {$_.state -eq 'Enabled'} | Select-Object displayNameListar todos los recursos dentro de una suscripción.
# Obtener recursos de una suscripción específica
$resources = Get-AzRA-ResourcesBySubscriptionID -AccessToken $azToken -SubscriptionID 'b413826f-108d-4049-8c11-d52d5d348768'
# Filtrar solo máquinas virtuales
$vms = $resources | Where-Object {$_.type -eq 'Microsoft.Compute/virtualMachines'}
$vms | Select-Object name, location, resourceGroup
# Agrupar recursos por tipo
$resources | Group-Object type | Select-Object Count, Name | Sort-Object Count -DescendingObtener permisos y asignaciones de roles para un recurso.
# Obtener permisos de un resource group
$rgPath = '/subscriptions/xxx-xxx-xxx/resourceGroups/Production'
$permissions = Get-AzRA-RoleAssignment -AccessToken $azToken -ResourcePath $rgPath
# Obtener permisos de una VM específica
$vmPath = '/subscriptions/b413826f-108d-4049-8c11-d52d5d348768/resourceGroups/Test/providers/Microsoft.Compute/virtualMachines/myVM'
$vmPerms = Get-AzRA-RoleAssignment -AccessToken $azToken -ResourcePath $vmPath
$vmPerms | Select-Object actions, notActionsListar todos los usuarios del tenant con paginación automática.
# Obtener todos los usuarios
$users = Get-AzRA-Users -AccessToken $graphToken
# Buscar administradores
$admins = $users | Where-Object {$_.userPrincipalName -like "*admin*"}
$admins | Select-Object userPrincipalName, displayName, mail
# Filtrar usuarios por dominio
$users | Where-Object {$_.userPrincipalName -like "*@contoso.com"} | Select-Object displayName, mail
# Contar usuarios totales
Write-Output "Total de usuarios: $($users.Count)"Listar todas las aplicaciones empresariales (registros de aplicación).
# Obtener todas las aplicaciones
$apps = Get-AzRA-EnterpriseApplications -AccessToken $graphToken
# Mostrar información básica
$apps | Select-Object displayName, appId, publisherDomain
# Buscar aplicaciones por nombre
$apps | Where-Object {$_.displayName -like "*Microsoft*"} | Select-Object displayName, appId
# Contar aplicaciones
Write-Output "Total de aplicaciones: $($apps.Count)"Obtener asignaciones de roles de aplicación para un service principal.
# Obtener role assignments de un service principal
$spId = '2830a3fe-846b-4008-b8e5-bbe6255488a8'
$roleAssignments = Get-AzRA-AppRoleAssignment -AccessToken $graphToken -ServicePrincipalId $spId
# Mostrar asignaciones
$roleAssignments | Select-Object principalDisplayName, principalType, appRoleIdObtener todos los grupos y roles de directorio de un usuario.
# Obtener membresías de un usuario
$userGroups = Get-AzRA-RolesGroupsByEmail -AccessToken $graphToken -Email 'usuario@contoso.com'
# Filtrar solo grupos
$groups = $userGroups | Where-Object {$_.'@odata.type' -eq '#microsoft.graph.group'}
$groups | Select-Object displayName, mail
# Filtrar solo roles de directorio
$roles = $userGroups | Where-Object {$_.'@odata.type' -eq '#microsoft.graph.directoryRole'}
$roles | Select-Object displayName, description
# Contar membresías totales
Write-Output "Total de grupos/roles: $($userGroups.Count)"Hacer peticiones personalizadas a cualquier endpoint de Azure o Microsoft Graph.
# Petición personalizada a Azure Management API
$customUri = 'https://management.azure.com/subscriptions?api-version=2020-01-01'
$result = Invoke-AzRA-APIRequest -AccessToken $azToken -Uri $customUri
# Petición a Microsoft Graph con paginación
$customGraphUri = 'https://graph.microsoft.com/v1.0/groups'
$groups = Invoke-AzRA-APIRequest -AccessToken $graphToken -Uri $customGraphUri -EnablePagination
$groups | Select-Object displayName, mail
# Petición a un endpoint específico de Graph
$deviceUri = 'https://graph.microsoft.com/v1.0/devices'
$devices = Invoke-AzRA-APIRequest -AccessToken $graphToken -Uri $deviceUri -EnablePagination# 1. Importar módulo
Import-Module .\AzRA.psd1
# 2. Obtener tokens
$azToken = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
$graphToken = (az account get-access-token --resource https://graph.microsoft.com | ConvertFrom-Json).accessToken
# 3. Enumerar suscripciones
$subs = Get-AzRA-Subscriptions -AccessToken $azToken
Write-Output "Suscripciones encontradas: $($subs.Count)"
# 4. Enumerar recursos de cada suscripción
foreach ($sub in $subs) {
Write-Output "`nAnalizando suscripción: $($sub.displayName)"
$resources = Get-AzRA-ResourcesBySubscriptionID -AccessToken $azToken -SubscriptionID $sub.subscriptionId
Write-Output " Recursos encontrados: $($resources.Count)"
# Mostrar resumen por tipo
$resources | Group-Object type | Select-Object Count, Name | Sort-Object Count -Descending | Format-Table
}
# 5. Enumerar usuarios
$users = Get-AzRA-Users -AccessToken $graphToken
Write-Output "`nUsuarios encontrados: $($users.Count)"
# 6. Buscar usuarios privilegiados
$privilegedUsers = $users | Where-Object {
$_.userPrincipalName -like "*admin*" -or
$_.displayName -like "*admin*"
}
Write-Output "Usuarios potencialmente privilegiados: $($privilegedUsers.Count)"
$privilegedUsers | Select-Object userPrincipalName, displayName | Format-Table
# 7. Enumerar aplicaciones
$apps = Get-AzRA-EnterpriseApplications -AccessToken $graphToken
Write-Output "`nAplicaciones encontradas: $($apps.Count)"Audita el historial de deployments de Azure buscando parámetros ARM que contengan credenciales o secretos en texto claro.
Cómo funciona:
- Verifica que existe una sesión Az activa (
Connect-AzAccount) - Itera sobre todas las suscripciones accesibles (o una específica)
- Por cada suscripción → resource groups → deployments
- Inspecciona
$dep.Parameters.Keysbuscando coincidencias con keywords (password,secret,key,token, etc.) - Filtra parámetros de tipo
SecureStringy valores falsos positivos (null,true,false,none...) - Devuelve los hallazgos al pipeline como objetos y opcionalmente los exporta a CSV
Retry automático: ante errores HTTP 429 (throttling) o errores transitorios 5xx, reintenta automáticamente con backoff lineal (espera RetryDelaySec × intento).
Permisos requeridos:
Microsoft.Resources/deployments/readMicrosoft.Resources/subscriptions/resourceGroups/read
# Escanear todas las suscripciones
Connect-AzAccount
Get-AzRA-DeploymentParameterSecrets
# Escanear solo una suscripción
Get-AzRA-DeploymentParameterSecrets -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Exportar hallazgos a CSV (nombre auto-generado con timestamp)
Get-AzRA-DeploymentParameterSecrets -OutputPath 'C:\Reports'
# → C:\Reports\AzRA-DeploymentSecrets_20250406-1530.csv
# Exportar CSV y volcar todos los deployments en JSON para revisión manual
Get-AzRA-DeploymentParameterSecrets -OutputPath 'C:\Reports' -DumpRaw
# → C:\Reports\AzRA-DeploymentSecrets_20250406-1530.csv
# → C:\Reports\DeploymentTemplatesRawDump\<Suscripcion>\<ResourceGroup>\<Deployment>.json
# Filtrar por resource group
Get-AzRA-DeploymentParameterSecrets | Where-Object { $_.'Grupo de recursos' -eq 'Production' }
# Keywords personalizadas
Get-AzRA-DeploymentParameterSecrets -Keywords @('connectionstring', 'storageaccountkey', 'sas')
# Ajustar reintentos ante throttling
Get-AzRA-DeploymentParameterSecrets -MaxRetries 5 -RetryDelaySec 10Parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-SubscriptionId |
string |
Limita el escaneo a una suscripción concreta. Si se omite, escanea todas |
-Keywords |
string[] |
Lista de keywords para buscar en nombres de parámetros. Default: password, secret, admin, key, pwd, cred, token, auth |
-OutputPath |
string |
Carpeta de salida. El CSV se genera con timestamp automático |
-DumpRaw |
switch |
Vuelca los objetos raw de cada deployment como JSON. Requiere -OutputPath |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Campos de cada hallazgo (CSV y pipeline):
| Campo | Descripción |
|---|---|
Nombre suscripcion |
Nombre de la suscripción |
ID suscripcion |
GUID de la suscripción |
Grupo de recursos |
Resource group donde está el deployment |
Deployment Name |
Nombre del deployment |
Parametros |
JSON con Name, Value y Type del parámetro encontrado |
Estructura del dump raw (-DumpRaw):
<OutputPath>/
DeploymentTemplatesRawDump/
<Suscripcion>/
<ResourceGroup>/
<DeploymentName>.json ← objeto completo de Get-AzResourceGroupDeployment
# 1. Importar módulo y autenticar
Import-Module .\AzRA.psd1
Connect-AzAccount
# 2. Buscar credenciales en historial de deployments
$secrets = Get-AzRA-DeploymentParameterSecrets -OutputPath 'C:\Reports\secrets.csv'
Write-Output "Hallazgos encontrados: $($secrets.Count)"
# 3. Ver hallazgos agrupados por suscripción
$secrets | Group-Object 'Nombre suscripcion' | Select-Object Count, Name | Sort-Object Count -Descending
# 4. Extraer solo los valores (para revisión manual)
$secrets | ForEach-Object { $_.Parametros | ConvertFrom-Json } | Select-Object Name, Value, TypeEnumera todas las cuentas de Azure Automation accesibles, descarga el código fuente de cada Runbook y opcionalmente escanea el contenido buscando credenciales hardcodeadas.
Cómo funciona:
- Itera sobre todas las suscripciones accesibles (o una específica)
- Por cada suscripción enumera todas las Automation Accounts via Azure Management API
- Por cada cuenta obtiene la lista de Runbooks (con paginación automática)
- Descarga el contenido de cada Runbook con retry automático ante throttling
- Opcionalmente guarda los archivos en disco con jerarquía por suscripción y cuenta
- Opcionalmente escanea el contenido buscando asignaciones de credenciales en texto claro
- Devuelve un objeto por Runbook al pipeline
Retry automático: ante errores HTTP 429 (throttling) o errores transitorios 5xx, reintenta automáticamente con backoff lineal.
Permisos requeridos:
Microsoft.Automation/automationAccounts/readMicrosoft.Automation/automationAccounts/runbooks/readMicrosoft.Automation/automationAccounts/runbooks/content/read
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Enumerar todos los Runbooks (solo metadatos al pipeline)
Get-AzRA-AutomationRunbooks -AccessToken $token
# Escanear solo una suscripción
Get-AzRA-AutomationRunbooks -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Descargar archivos y exportar CSV de metadatos
Get-AzRA-AutomationRunbooks -AccessToken $token -OutputPath 'C:\Audit'
# → C:\Audit\AutomationRunbooks\<Suscripcion>\<Cuenta>\<Runbook>.ps1
# → C:\Audit\AzRA-AutomationRunbooks_20250406-1530.csv
# Descargar y escanear credenciales hardcodeadas
Get-AzRA-AutomationRunbooks -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets
# → C:\Audit\AzRA-AutomationRunbooks-Secrets_20250406-1530.csv
# Filtrar en pipeline solo los Runbooks con secrets
Get-AzRA-AutomationRunbooks -AccessToken $token -ScanSecrets |
Where-Object { $_.HasSecrets } |
Select-Object RunbookName, AutomationAccount, SecretFindings
# Keywords personalizadas
Get-AzRA-AutomationRunbooks -AccessToken $token -ScanSecrets `
-Keywords @('storageaccountkey', 'sqlpassword', 'clientsecret')Parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-AccessToken |
string |
Token Bearer de Azure Management API (obligatorio) |
-SubscriptionId |
string |
Limita el escaneo a una suscripción. Si se omite, escanea todas |
-OutputPath |
string |
Carpeta de salida para archivos y CSVs (nombres auto-generados con timestamp) |
-ScanSecrets |
switch |
Activa el scanner de credenciales hardcodeadas en el contenido |
-Keywords |
string[] |
Keywords para detectar asignaciones sensibles. Default: password, secret, key, token, credential, sas, connectionstring... |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Objeto devuelto por Runbook (pipeline):
| Campo | Descripción |
|---|---|
SubscriptionId / SubscriptionName |
Suscripción donde está la cuenta |
AutomationAccount |
Nombre de la Automation Account |
ResourceGroup |
Resource group de la cuenta |
RunbookName |
Nombre del Runbook |
RunbookType |
Tipo: PowerShell, PowerShell72, Python3, etc. |
RunbookState |
Estado: Published, Draft |
LastModified |
Fecha de última modificación |
ContentSizeBytes |
Tamaño del contenido en bytes |
FilePath |
Ruta del archivo guardado ($null si no se usó -OutputPath) |
SecretFindings |
Array de findings (Keyword, Line, MatchedLine). Vacío si -ScanSecrets no activo |
HasSecrets |
$true si se encontró algún finding |
Estructura de salida en disco (-OutputPath):
<OutputPath>/
AutomationRunbooks/
<Suscripcion>/
<AutomationAccount>/
<Runbook>.ps1 / .py / .txt ← extensión según RunbookType
AzRA-AutomationRunbooks_<timestamp>.csv
AzRA-AutomationRunbooks-Secrets_<timestamp>.csv ← solo si -ScanSecrets
Scanner de secrets (-ScanSecrets):
Busca asignaciones del patrón keyword = "valor" o keyword: 'valor' (case-insensitive). Filtra automáticamente variables PowerShell ($variable) y valores triviales (null, true, $null) para reducir falsos positivos.
# 1. Importar módulo
Import-Module .\AzRA.psd1
# 2. Obtener token
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# 3. Escaneo completo con descarga y búsqueda de secrets
$runbooks = Get-AzRA-AutomationRunbooks -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets
Write-Output "Runbooks encontrados: $($runbooks.Count)"
# 4. Ver resumen de secrets por cuenta
$runbooks | Where-Object { $_.HasSecrets } |
Group-Object AutomationAccount |
Select-Object Name, Count |
Sort-Object Count -Descending
# 5. Ver todos los findings en detalle
$runbooks | Where-Object { $_.HasSecrets } | ForEach-Object {
Write-Output "`n[$($_.AutomationAccount)] $($_.RunbookName)"
$_.SecretFindings | ForEach-Object {
Write-Output " Line $($_.Line) [$($_.Keyword)]: $($_.MatchedLine)"
}
}Enumera todas las Logic Apps accesibles, descarga su definición completa (triggers, acciones, parámetros del workflow) y opcionalmente las escanea en busca de credenciales hardcodeadas, endpoints expuestos y configuraciones sensibles.
Cómo funciona:
- Itera sobre todas las suscripciones accesibles (o una específica)
- Lista todas las Logic Apps de cada suscripción via Azure Management API (con paginación)
- Descarga la definición completa de cada Logic App con retry automático ante throttling
- Opcionalmente vuelca el JSON completo a disco en jerarquía por suscripción y RG
- Opcionalmente escanea 4 fuentes en busca de información sensible (ver abajo)
- Opcionalmente recupera y analiza el historial de versiones de cada Logic App
- Devuelve un objeto por Logic App al pipeline con metadatos de superficie de ataque
Retry automático: ante errores HTTP 429 (throttling) o errores transitorios 5xx, reintenta con backoff lineal.
Permisos requeridos:
Microsoft.Logic/workflows/read
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Enumerar todas las Logic Apps (metadatos y superficie de ataque al pipeline)
Get-AzRA-LogicApps -AccessToken $token
# Escanear solo una suscripción
Get-AzRA-LogicApps -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Volcar JSONs y exportar CSV de metadatos
Get-AzRA-LogicApps -AccessToken $token -OutputPath 'C:\Audit'
# → C:\Audit\LogicAppsRawDump\<Suscripcion>\<ResourceGroup>\<LogicApp>.json
# → C:\Audit\AzRA-LogicApps_20250406-1530.csv
# Escaneo completo con búsqueda de secrets
Get-AzRA-LogicApps -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets
# → C:\Audit\AzRA-LogicApps-Secrets_20250406-1530.csv
# Incluir historial de versiones en el análisis
Get-AzRA-LogicApps -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets -IncludeVersions
# → C:\Audit\LogicAppsRawDump\<Sub>\<RG>\<LogicApp>_versions.json
# Filtrar Logic Apps con triggers expuestos al exterior (tipo Request)
Get-AzRA-LogicApps -AccessToken $token |
Where-Object { $_.HasExposedTrigger } |
Select-Object LogicAppName, ResourceGroup, TriggerTypes, ExposedEndpoints
# Filtrar Logic Apps con parámetros SecureString (credenciales declaradas)
Get-AzRA-LogicApps -AccessToken $token |
Where-Object { $_.SecureStringParamCount -gt 0 } |
Select-Object LogicAppName, ResourceGroup, SecureStringParamCount
# Filtrar Logic Apps con findings de secrets
Get-AzRA-LogicApps -AccessToken $token -ScanSecrets |
Where-Object { $_.HasSecrets } |
Select-Object LogicAppName, ResourceGroup, SecretFindings
# Keywords personalizadas
Get-AzRA-LogicApps -AccessToken $token -ScanSecrets `
-Keywords @('storageaccountkey', 'sqlconnection', 'apimsubscriptionkey')Parámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-AccessToken |
string |
Token Bearer de Azure Management API (obligatorio) |
-SubscriptionId |
string |
Limita el escaneo a una suscripción. Si se omite, escanea todas |
-OutputPath |
string |
Carpeta de salida para JSONs y CSVs (nombres auto-generados con timestamp) |
-ScanSecrets |
switch |
Activa el scanner de credenciales en 4 fuentes de la definición |
-IncludeVersions |
switch |
Recupera y analiza el historial de versiones de cada Logic App |
-Keywords |
string[] |
Keywords para la detección. Default: password, secret, key, token, sas, bearer, authorization, ocp-apim... |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Objeto devuelto por Logic App (pipeline):
| Campo | Descripción |
|---|---|
SubscriptionId / SubscriptionName |
Suscripción donde está la Logic App |
ResourceGroup |
Resource group |
LogicAppName |
Nombre de la Logic App |
Location |
Región Azure |
State |
Estado: Enabled, Disabled, Suspended |
CreatedTime / ChangedTime |
Fechas de creación y última modificación |
TriggerCount |
Número total de triggers definidos |
TriggerTypes |
Tipos de trigger: Request, Recurrence, ApiConnection, etc. |
HasExposedTrigger |
$true si hay algún trigger de tipo Request (acepta conexiones HTTP entrantes) |
ExposedEndpoints |
IPs de accessEndpointIpAddresses si están configuradas |
ActionCount |
Número total de acciones (incluyendo anidadas en Scope/If/Foreach) |
HttpActionCount |
Número de acciones de tipo Http (llamadas salientes) |
HasHttpActions |
$true si hay acciones HTTP |
WorkflowParameterCount |
Total de parámetros del workflow |
SecureStringParamCount |
Parámetros de tipo SecureString |
PlaintextParamCount |
Parámetros con valor potencialmente visible |
Tags |
Hashtable de tags del recurso |
VersionCount |
Número de versiones en historial (-1 si no se usó -IncludeVersions) |
SecretFindings |
Array de findings. Vacío si -ScanSecrets no activo |
HasSecrets |
$true si hay al menos un finding |
RawFilePath |
Ruta al JSON guardado ($null si no se usó -OutputPath) |
Scanner de secrets (-ScanSecrets) — 4 fuentes:
| Fuente | Qué analiza | Tipo de finding |
|---|---|---|
WorkflowParam |
properties.parameters — parámetros del workflow |
SecureString (reportado siempre), PlainText (por keyword en el nombre) |
HttpAction |
Acciones de tipo Http — URI, headers, bloque authentication, body |
HttpUri, HttpHeader, HttpAuth, HttpBody |
Action |
Cualquier otro tipo de acción — inputs serializado |
ActionInput |
Trigger |
Triggers ApiConnection con inputs sospechosos |
TriggerInput |
Tag |
Key + value de los tags del recurso | TagValue |
Los parámetros SecureString se reportan siempre aunque el valor esté enmascarado — su existencia ya es información relevante para el reconocimiento. Las expresiones dinámicas de Logic Apps (@{body()}, @{outputs()}) se filtran automáticamente para evitar falsos positivos.
Estructura de salida en disco (-OutputPath):
<OutputPath>/
LogicAppsRawDump/
<Suscripcion>/
<ResourceGroup>/
<LogicApp>.json ← definición completa (properties.definition + parameters)
<LogicApp>_versions.json ← solo si -IncludeVersions
AzRA-LogicApps_<timestamp>.csv
AzRA-LogicApps-Secrets_<timestamp>.csv ← solo si -ScanSecrets
# 1. Importar módulo y obtener token
Import-Module .\AzRA.psd1
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# 2. Escaneo completo
$logicApps = Get-AzRA-LogicApps -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets -IncludeVersions
Write-Output "Logic Apps encontradas: $($logicApps.Count)"
# 3. Resumen de superficie de ataque
Write-Output "`n--- Triggers expuestos (Request) ---"
$logicApps | Where-Object { $_.HasExposedTrigger } |
Select-Object LogicAppName, ResourceGroup, ExposedEndpoints | Format-Table
Write-Output "`n--- Logic Apps con acciones HTTP salientes ---"
$logicApps | Where-Object { $_.HasHttpActions } |
Select-Object LogicAppName, ResourceGroup, HttpActionCount |
Sort-Object HttpActionCount -Descending | Format-Table
# 4. Resumen de findings
Write-Output "`n--- Findings de credenciales ---"
$logicApps | Where-Object { $_.HasSecrets } |
Group-Object ResourceGroup |
Select-Object Name, Count | Sort-Object Count -Descending | Format-Table
# 5. Ver findings en detalle
$logicApps | Where-Object { $_.HasSecrets } | ForEach-Object {
Write-Output "`n[$($_.ResourceGroup)] $($_.LogicAppName)"
$_.SecretFindings | ForEach-Object {
Write-Output " [$($_.Source)/$($_.FindingType)] $($_.PropertyPath) → $($_.MatchedValue)"
}
}Enumera todas las Storage Accounts accesibles, evalúa cada una contra un conjunto de checks de seguridad críticos, altos e informativos, detecta contenedores con acceso público y opcionalmente extrae las claves de acceso.
Cómo funciona:
- Itera sobre todas las suscripciones accesibles (o una específica)
- Lista todas las Storage Accounts de cada suscripción (con paginación ARM)
- Por cada cuenta evalúa los checks de seguridad directamente desde
properties - Lista todos los contenedores blob de la cuenta (con paginación)
- Para contenedores con acceso
Container(nivel más alto): realiza listado anónimo de blobs sin token para demostrar el impacto real - Si
-ScanSecretsy shared key access está habilitado: extrae las claves de acceso via POSTlistKeys - Opcionalmente vuelca los JSONs a disco y exporta CSVs con timestamp
- Devuelve un objeto por cuenta al pipeline
Retry automático: ante errores HTTP 429 (throttling) o errores transitorios 5xx, reintenta con backoff lineal.
Permisos requeridos:
Microsoft.Storage/storageAccounts/readMicrosoft.Storage/storageAccounts/blobServices/containers/readMicrosoft.Storage/storageAccounts/listkeys/action(solo para-ScanSecrets)
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Enumerar todas las cuentas (metadatos y checks de seguridad al pipeline)
Get-AzRA-StorageAccounts -AccessToken $token
# Escanear solo una suscripción
Get-AzRA-StorageAccounts -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Volcar JSONs y exportar CSV de metadatos
Get-AzRA-StorageAccounts -AccessToken $token -OutputPath 'C:\Audit'
# → C:\Audit\StorageAccountsRawDump\<Suscripcion>\<Cuenta>\account.json
# → C:\Audit\StorageAccountsRawDump\<Suscripcion>\<Cuenta>\containers.json
# → C:\Audit\AzRA-StorageAccounts_20250406-1530.csv
# Extraer claves de acceso y connection strings
Get-AzRA-StorageAccounts -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets
# → C:\Audit\AzRA-StorageAccounts-Secrets_20250406-1530.csv
# Filtrar cuentas con contenedores públicos (Container-level) y ver blobs expuestos
Get-AzRA-StorageAccounts -AccessToken $token |
Where-Object { $_.HasPublicContainers } |
Select-Object StorageAccountName, ResourceGroup, PublicContainerNames, AnonymousBlobCount, AnonymousBlobs
# Filtrar cuentas con mayor riesgo: sin firewall Y shared key habilitado
Get-AzRA-StorageAccounts -AccessToken $token |
Where-Object { $_.NoFirewall -and $_.SharedKeyAccessEnabled } |
Select-Object StorageAccountName, ResourceGroup, SubscriptionName
# Ver todas las misconfiguraciones ordenadas por criticidad
Get-AzRA-StorageAccounts -AccessToken $token |
Select-Object StorageAccountName, ResourceGroup,
HasPublicContainers, NoFirewall, SharedKeyAccessEnabled,
HttpsNotEnforced, WeakTlsVersion, NoKeyExpirationPolicy |
Sort-Object HasPublicContainers, NoFirewall -Descending | Format-TableParámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-AccessToken |
string |
Token Bearer de Azure Management API (obligatorio) |
-SubscriptionId |
string |
Limita el escaneo a una suscripción. Si se omite, escanea todas |
-OutputPath |
string |
Carpeta de salida para JSONs y CSVs (nombres auto-generados con timestamp) |
-ScanSecrets |
switch |
Extrae las claves de acceso via listKeys (solo si shared key está habilitado) |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Checks de seguridad evaluados:
| Severidad | Campo | Condición de riesgo |
|---|---|---|
| Crítico | HasPublicContainers |
Algún contenedor con publicAccess = "Container" (listado + lectura anónima sin token) |
| Crítico | HasBlobPublicContainers |
Algún contenedor con publicAccess = "Blob" (lectura si se conoce la URL) |
| Crítico | NoFirewall |
networkAcls.defaultAction = "Allow" — cualquier IP puede acceder |
| Crítico | SharedKeyAccessEnabled |
allowSharedKeyAccess no es false explícito (null = habilitado en cuentas antiguas) |
| Alto | HttpsNotEnforced |
supportsHttpsTrafficOnly = false — permite tráfico HTTP sin cifrar. Nota: este campo es un flag de hallazgo (true = hay problema), no una copia directa de la propiedad Azure. Ver HttpsTrafficOnlyEnabled para el valor raw. |
| Alto | WeakTlsVersion |
minimumTlsVersion es TLS1_0 o TLS1_1 |
| Alto | NoKeyExpirationPolicy |
keyPolicy es null — las claves no tienen rotación obligatoria |
| Alto | NoSasExpirationPolicy |
sasPolicy es null — los SAS tokens no tienen expiración forzada |
| Alto | BlobPublicAccessAllowedAtAccount |
allowBlobPublicAccess = true — la cuenta permite contenedores públicos |
| Info | FirewallBypassAzureServices |
El firewall permite bypass a servicios Azure |
| Info | NoCustomerManagedKeys |
Cifrado con claves de Microsoft, no del cliente (encryption.keySource) |
Objeto devuelto por cuenta (pipeline):
| Campo | Descripción |
|---|---|
SubscriptionId / SubscriptionName |
Suscripción donde está la cuenta |
ResourceGroup |
Resource group |
StorageAccountName |
Nombre de la Storage Account |
Location, Kind, Sku |
Región, tipo y SKU |
CreationTime |
Fecha de creación |
HasPublicContainers / HasBlobPublicContainers |
Flags de contenedores públicos |
NoFirewall, SharedKeyAccessEnabled, ... |
Todos los checks de seguridad (bool) |
HttpsTrafficOnlyEnabled |
Espejo directo de supportsHttpsTrafficOnly de la API Azure. $true = HTTPS forzado (sin hallazgo). Usar este campo para comparar con el valor visto en el portal. |
MinimumTlsVersion, NetworkDefaultAction, EncryptionKeySource |
Valores raw de configuración |
ContainerCount / PublicContainerCount / BlobPublicContainerCount |
Conteos de contenedores |
PublicContainerNames |
Nombres de contenedores Container-level (comma-separated) |
AnonymousBlobCount / AnonymousBlobs |
Blobs encontrados via listado anónimo sin token |
KeyFindings |
Array de objetos {KeyName, KeyValue, Permissions, ConnectionString} si -ScanSecrets |
HasKeyFindings |
$true si se extrajeron claves |
RawFilePath |
Directorio del dump ($null si no se usó -OutputPath) |
Listado anónimo de blobs:
Cuando un contenedor tiene publicAccess = "Container", la función llama automáticamente a https://{account}.blob.core.windows.net/{container}?restype=container&comp=list&maxresults=100 sin token de autenticación para demostrar el impacto real. Los nombres de los blobs encontrados se incluyen en AnonymousBlobs. Si el listado falla (restricciones de red adicionales), se registra como Write-Verbose y no interrumpe el análisis.
Estructura de salida en disco (-OutputPath):
<OutputPath>/
StorageAccountsRawDump/
<Suscripcion>/
<StorageAccount>/
account.json ← ARM object completo de la cuenta
containers.json ← lista de contenedores con publicAccess
AzRA-StorageAccounts_<timestamp>.csv
AzRA-StorageAccounts-Secrets_<timestamp>.csv ← solo si -ScanSecrets
CSV de secrets (-ScanSecrets):
| Campo | Descripción |
|---|---|
StorageAccountName |
Nombre de la cuenta |
KeyName |
key1 o key2 |
KeyValue |
Valor de la clave en texto claro |
Permissions |
Permisos: Full |
ConnectionString |
DefaultEndpointsProtocol=https;AccountName=...;AccountKey=...;EndpointSuffix=core.windows.net |
# 1. Importar módulo y obtener token
Import-Module .\AzRA.psd1
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# 2. Escaneo completo
$storage = Get-AzRA-StorageAccounts -AccessToken $token -OutputPath 'C:\Audit' -ScanSecrets
Write-Output "Storage Accounts encontradas: $($storage.Count)"
# 3. Resumen de criticidad
Write-Output "`n--- Contenedores públicos (CRÍTICO) ---"
$storage | Where-Object { $_.HasPublicContainers } |
Select-Object StorageAccountName, ResourceGroup, PublicContainerNames, AnonymousBlobCount |
Format-Table
Write-Output "`n--- Sin firewall (CRÍTICO) ---"
$storage | Where-Object { $_.NoFirewall } |
Select-Object StorageAccountName, ResourceGroup, SubscriptionName | Format-Table
Write-Output "`n--- TLS débil o HTTPS no forzado (ALTO) ---"
# HttpsNotEnforced = true significa hallazgo (HTTPS NO forzado)
# HttpsTrafficOnlyEnabled = valor raw de la API (true = HTTPS forzado, sin problema)
$storage | Where-Object { $_.WeakTlsVersion -or $_.HttpsNotEnforced } |
Select-Object StorageAccountName, MinimumTlsVersion, HttpsNotEnforced, HttpsTrafficOnlyEnabled | Format-Table
# 4. Ver claves extraídas
$storage | Where-Object { $_.HasKeyFindings } | ForEach-Object {
Write-Output "`n[$($_.ResourceGroup)] $($_.StorageAccountName)"
$_.KeyFindings | ForEach-Object {
Write-Output " $($_.KeyName): $($_.ConnectionString)"
}
}Enumera todos los Azure Key Vaults accesibles y los evalúa contra un conjunto de checks de seguridad críticos desde perspectiva de pentester, incluyendo configuración de recuperación, acceso de red, modelo de autorización y superficie de ataque expandida. Opcionalmente lista el inventario de secretos, claves y certificados vía Data Plane.
Cómo funciona:
- Itera sobre todas las suscripciones accesibles (o una específica)
- Lista todos los Key Vaults de cada suscripción (con paginación ARM). Nota: el listado inicial no incluye
propertiescompletas — se hace una llamada adicional por vault para obteneraccessPolicies,networkAcls,softDelete, etc. - Por cada vault evalúa todos los checks de seguridad desde
properties - Llama al endpoint de Diagnostic Settings (non-fatal) para detectar ausencia de audit logging
- Si
-ScanSecrets+-VaultToken: llama al Data Plane (vault.azure.net) para listar secretos, claves y certificados — solo metadatos (nombres, estado, expiración), no valores - Opcionalmente vuelca los JSONs a disco y exporta CSVs con timestamp
Tokens necesarios:
-AccessToken: scopehttps://management.azure.com/— Management Plane-VaultToken(opcional, solo con-ScanSecrets): scopehttps://vault.azure.net/— Data Plane
Permisos requeridos:
Microsoft.KeyVault/vaults/readMicrosoft.Insights/diagnosticSettings/read(opcional — non-fatal si falta)- Data Plane:
Secret List,Key List,Certificate List(solo con-ScanSecrets)
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Enumerar todos los vaults (management plane, sin data plane)
Get-AzRA-KeyVaults -AccessToken $token
# Escanear solo una suscripción
Get-AzRA-KeyVaults -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Volcar JSONs y exportar CSV
Get-AzRA-KeyVaults -AccessToken $token -OutputPath 'C:\Audit'
# → C:\Audit\KeyVaultsRawDump\<Suscripcion>\<Vault>\vault.json
# → C:\Audit\KeyVaultsRawDump\<Suscripcion>\<Vault>\diagnostics.json
# → C:\Audit\AzRA-KeyVaults_20250406-1530.csv
# Data plane: listar inventario de secretos/claves/certificados (requiere vault token)
$vaultToken = (az account get-access-token --resource https://vault.azure.net/ | ConvertFrom-Json).accessToken
Get-AzRA-KeyVaults -AccessToken $token -VaultToken $vaultToken -ScanSecrets -OutputPath 'C:\Audit'
# → C:\Audit\AzRA-KeyVaults-Secrets_20250406-1530.csv
# Filtrar vaults con misconfiguraciones críticas
Get-AzRA-KeyVaults -AccessToken $token |
Where-Object { $_.NotRecoverable -or $_.PublicNetworkAccess -or $_.LegacyAccessPolicies } |
Select-Object VaultName, ResourceGroup, NotRecoverable, PublicNetworkAccess, LegacyAccessPolicies
# Filtrar vaults con access policies excesivamente permisivas
Get-AzRA-KeyVaults -AccessToken $token |
Where-Object { $_.OverlyPermissiveAccessPolicies } |
Select-Object VaultName, ResourceGroup, AccessPolicyCount, RbacEnabled
# Filtrar vaults que expanden la superficie de ataque (deployment/disk/template)
Get-AzRA-KeyVaults -AccessToken $token |
Where-Object { $_.EnabledForDeployment -or $_.EnabledForTemplateDeployment } |
Select-Object VaultName, ResourceGroup, EnabledForDeployment, EnabledForTemplateDeployment
# Filtrar por secretos expirados (solo con -ScanSecrets -VaultToken)
Get-AzRA-KeyVaults -AccessToken $token -VaultToken $vaultToken -ScanSecrets |
Where-Object { $_.HasExpiredSecrets } |
Select-Object VaultName, ResourceGroup, SecretCountParámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-AccessToken |
string |
Token Bearer de Azure Management API (obligatorio) |
-SubscriptionId |
string |
Limita el escaneo a una suscripción. Si se omite, escanea todas |
-OutputPath |
string |
Carpeta de salida para JSONs y CSVs (nombres auto-generados con timestamp) |
-VaultToken |
string |
Token Bearer de Key Vault Data Plane (https://vault.azure.net/). Requerido con -ScanSecrets |
-ScanSecrets |
switch |
Activa el listado de secretos/claves/certificados via Data Plane (requiere -VaultToken) |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Checks de seguridad evaluados:
| Severidad | Campo | Condición de riesgo |
|---|---|---|
| Crítico | NotRecoverable |
SoftDeleteDisabled OR PurgeProtectionDisabled — los secretos se pueden eliminar permanentemente |
| Crítico | SoftDeleteDisabled |
enableSoftDelete != true — sin periodo de retención ante borrado accidental/malicioso |
| Crítico | PurgeProtectionDisabled |
enablePurgeProtection != true — borrado definitivo posible sin esperar retención |
| Crítico | PublicNetworkAccess |
Red pública habilitada (Enabled) Y sin firewall (networkAcls.defaultAction = Allow) |
| Crítico | LegacyAccessPolicies |
enableRbacAuthorization != true — usa Access Policies (vault-based), no RBAC de Azure |
| Alto | OverlyPermissiveAccessPolicies |
Alguna policy tiene "all" o ≥ 8 permisos en secrets/keys/certificates |
| Alto | EnabledForDeployment |
Las VMs pueden recuperar secretos del vault como parte de su provisioning |
| Alto | EnabledForDiskEncryption |
El servicio Azure Disk Encryption puede leer claves del vault |
| Alto | EnabledForTemplateDeployment |
Los ARM templates pueden recuperar secretos durante el despliegue |
| Alto | WeakSoftDeleteRetention |
softDeleteRetentionInDays < 30 — ventana de recuperación insuficiente |
| Alto | NoFirewall |
networkAcls.defaultAction = Allow — sin restricción de red configurada |
| Alto | NoDiagnosticSettings |
Sin diagnostic settings — no hay audit logging de accesos al vault |
| Info | FirewallBypassAzureServices |
El firewall permite bypass a Azure Services |
| Info | NoPrivateEndpoint |
Sin private endpoint configurado |
| Info | StandardSku |
SKU Standard en lugar de Premium (sin HSM hardware) |
Objeto devuelto por vault (pipeline):
| Campo | Descripción |
|---|---|
SubscriptionId / SubscriptionName |
Suscripción |
ResourceGroup |
Resource group |
VaultName, Location, Sku, VaultUri, TenantId |
Identidad del vault |
NotRecoverable, SoftDeleteDisabled, PurgeProtectionDisabled |
Checks críticos de recuperación |
PublicNetworkAccess, LegacyAccessPolicies |
Checks críticos de acceso |
OverlyPermissiveAccessPolicies, EnabledForDeployment/DiskEncryption/TemplateDeployment |
Checks altos |
WeakSoftDeleteRetention, NoFirewall, NoDiagnosticSettings |
Checks altos |
FirewallBypassAzureServices, NoPrivateEndpoint, StandardSku |
Checks informativos |
SoftDeleteRetentionDays, NetworkDefaultAction, NetworkBypass |
Valores raw de configuración |
AccessPolicyCount, RbacEnabled, PublicNetworkAccessRaw |
Valores raw de autorización |
SecretCount |
Número de secretos/claves/certificados (solo con -ScanSecrets -VaultToken) |
SecretItems |
Array de {ItemType, ItemName, Enabled, Expires, ContentType} |
HasExpiredSecrets |
$true si algún secreto/clave ha expirado |
RawFilePath |
Directorio del dump ($null si no se usó -OutputPath) |
Nota sobre el token de Data Plane:
El token de Azure Management API y el token de Key Vault Data Plane tienen scopes distintos y no son intercambiables. Para activar -ScanSecrets se necesitan ambos tokens:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
$vaultToken = (az account get-access-token --resource https://vault.azure.net/ | ConvertFrom-Json).accessTokenEl Data Plane solo devuelve metadatos (nombre, estado enabled/disabled, fecha de expiración). Los valores reales de los secretos NO se recuperan.
Estructura de salida en disco (-OutputPath):
<OutputPath>/
KeyVaultsRawDump/
<Suscripcion>/
<VaultName>/
vault.json ← ARM object completo (ConvertTo-Json -Depth 20)
diagnostics.json ← diagnostic settings (si se obtuvo)
AzRA-KeyVaults_<timestamp>.csv
AzRA-KeyVaults-Secrets_<timestamp>.csv ← solo si -ScanSecrets + -VaultToken
CSV de data plane (-ScanSecrets -VaultToken):
| Campo | Descripción |
|---|---|
VaultName |
Nombre del vault |
ItemType |
secret, key o certificate |
ItemName |
Nombre del item |
ItemId |
URI completo del item en el data plane |
Enabled |
$true / $false |
Expires |
Fecha de expiración (UTC) o $null si no tiene |
ContentType |
Content type del secreto (si aplica) |
# 1. Importar módulo y obtener tokens
Import-Module .\AzRA.psd1
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
$vaultToken = (az account get-access-token --resource https://vault.azure.net/ | ConvertFrom-Json).accessToken
# 2. Escaneo completo con data plane
$vaults = Get-AzRA-KeyVaults -AccessToken $token -VaultToken $vaultToken -ScanSecrets -OutputPath 'C:\Audit'
Write-Output "Key Vaults encontrados: $($vaults.Count)"
# 3. Resumen por severidad
Write-Output "`n--- CRÍTICO: No recuperables ---"
$vaults | Where-Object { $_.NotRecoverable } |
Select-Object VaultName, ResourceGroup, SoftDeleteDisabled, PurgeProtectionDisabled | Format-Table
Write-Output "`n--- CRÍTICO: Acceso de red público ---"
$vaults | Where-Object { $_.PublicNetworkAccess } |
Select-Object VaultName, ResourceGroup, NetworkDefaultAction | Format-Table
Write-Output "`n--- CRÍTICO: Access Policies (no RBAC) ---"
$vaults | Where-Object { $_.LegacyAccessPolicies } |
Select-Object VaultName, ResourceGroup, AccessPolicyCount, OverlyPermissiveAccessPolicies | Format-Table
Write-Output "`n--- ALTO: Superficie de ataque expandida ---"
$vaults | Where-Object { $_.EnabledForDeployment -or $_.EnabledForTemplateDeployment -or $_.EnabledForDiskEncryption } |
Select-Object VaultName, ResourceGroup, EnabledForDeployment, EnabledForDiskEncryption, EnabledForTemplateDeployment | Format-Table
# 4. Inventario de secretos expirados
$vaults | Where-Object { $_.HasExpiredSecrets } | ForEach-Object {
Write-Output "`n[$($_.ResourceGroup)] $($_.VaultName)"
$_.SecretItems | Where-Object { $_.Expires -and $_.Expires -lt (Get-Date) } | ForEach-Object {
Write-Output " [$($_.ItemType)] $($_.ItemName) — expiró: $($_.Expires)"
}
}Enumera todas las Azure Virtual Machines y SQL Server on Azure VMs (IaaS) accesibles, evalúa cada una contra checks de seguridad críticos y detecta exposición real de red correlacionando la cadena VM → NIC → IP Pública → NSG. Las SQL VMs se correlacionan automáticamente con su VM subyacente enriqueciendo el mismo objeto de resultado.
Cómo funciona:
- Itera sobre todas las suscripciones accesibles (o una específica)
- Pre-carga el índice de SQL VMs por suscripción (una sola llamada API) para correlación posterior
- Lista todas las Compute VMs de la suscripción (con paginación ARM)
- Por cada VM:
- Lista las extensiones instaladas
- Resuelve la cadena NIC → Public IP para detectar IPs públicas reales
- Obtiene reglas NSG asociadas a cada NIC para evaluar puertos abiertos (RDP, SSH, WinRM, SQL)
- Correlaciona con el índice de SQL VMs usando el ARM ID
- Si
-IncludeInstanceView: llama al endpoint/instanceViewpara obtenerPowerStatey detalles del OS
- Evalúa todos los checks de seguridad
- Opcionalmente vuelca JSON a disco y exporta CSV
Retry automático: ante errores HTTP 429 (throttling) o errores transitorios 5xx, reintenta con backoff lineal.
Permisos requeridos:
Microsoft.Compute/virtualMachines/readMicrosoft.Compute/virtualMachines/extensions/readMicrosoft.SqlVirtualMachine/sqlVirtualMachines/readMicrosoft.Network/networkInterfaces/readMicrosoft.Network/publicIPAddresses/readMicrosoft.Network/networkSecurityGroups/read
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Enumerar todas las VMs (checks de seguridad y exposición de red al pipeline)
Get-AzRA-VirtualMachines -AccessToken $token
# Escanear solo una suscripción
Get-AzRA-VirtualMachines -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Volcar JSONs y exportar CSV
Get-AzRA-VirtualMachines -AccessToken $token -OutputPath 'C:\Audit'
# → C:\Audit\VirtualMachinesRawDump\<Suscripcion>\<VM>\vm.json
# → C:\Audit\VirtualMachinesRawDump\<Suscripcion>\<VM>\extensions.json
# → C:\Audit\VirtualMachinesRawDump\<Suscripcion>\<VM>\network.json
# → C:\Audit\VirtualMachinesRawDump\<Suscripcion>\<VM>\sqlvm.json (si SQL VM)
# → C:\Audit\AzRA-VirtualMachines_20250406-1530.csv
# Con instance view (PowerState + OS name — más lento a escala)
Get-AzRA-VirtualMachines -AccessToken $token -IncludeInstanceView
# Filtrar VMs con RDP o SSH expuesto a internet
Get-AzRA-VirtualMachines -AccessToken $token |
Where-Object { $_.RdpExposed -or $_.SshExposed } |
Select-Object VmName, ResourceGroup, OsType, PublicIpAddresses, OpenInboundPorts
# Filtrar VMs con extensiones de RCE instaladas
Get-AzRA-VirtualMachines -AccessToken $token |
Where-Object { $_.CustomScriptExtension -or $_.RunCommandExtension } |
Select-Object VmName, ResourceGroup, InstalledExtensions
# Filtrar VMs con IP pública y sin NSG (exposición desconocida)
Get-AzRA-VirtualMachines -AccessToken $token |
Where-Object { $_.HasPublicIp -and $_.OpenInboundPorts -eq 'unknown (no NSG)' } |
Select-Object VmName, ResourceGroup, PublicIpAddresses
# Filtrar SQL VMs con conectividad pública o auth mixta
Get-AzRA-VirtualMachines -AccessToken $token |
Where-Object { $_.IsSqlVm -and ($_.SqlPublicConnectivity -or $_.SqlMixedAuthEnabled) } |
Select-Object VmName, ResourceGroup, SqlImageSku, SqlConnectivity, PublicIpAddresses
# VMs sin identidad administrada (usan credenciales almacenadas)
Get-AzRA-VirtualMachines -AccessToken $token |
Where-Object { $_.NoManagedIdentity } |
Select-Object VmName, ResourceGroup, OsType, ManagedIdentityType
# Ver todas las misconfiguraciones críticas
Get-AzRA-VirtualMachines -AccessToken $token |
Where-Object { $_.HasPublicIp -or $_.CustomScriptExtension -or $_.OsDiskNotEncrypted } |
Select-Object VmName, ResourceGroup, HasPublicIp, RdpExposed, SshExposed,
CustomScriptExtension, OsDiskNotEncrypted |
Sort-Object HasPublicIp, RdpExposed -Descending | Format-TableParámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-AccessToken |
string |
Token Bearer de Azure Management API (obligatorio) |
-SubscriptionId |
string |
Limita el escaneo a una suscripción. Si se omite, escanea todas |
-OutputPath |
string |
Carpeta de salida para JSONs y CSV (nombre auto-generado con timestamp) |
-IncludeInstanceView |
switch |
Activa la llamada adicional /instanceView por VM para obtener PowerState y detalles del OS. Desactivado por defecto (coste significativo a escala) |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Checks de seguridad evaluados:
| Severidad | Campo | Condición de riesgo |
|---|---|---|
| Crítico | HasPublicIp |
La VM tiene al menos una IP pública asignada |
| Crítico | RdpExposed |
IP pública + NSG permite inbound puerto 3389 desde 0.0.0.0/0 |
| Crítico | SshExposed |
IP pública + NSG permite inbound puerto 22 desde 0.0.0.0/0 |
| Crítico | WinRmExposed |
IP pública + NSG permite inbound puertos 5985/5986 desde internet |
| Crítico | SqlPortExposed |
IP pública + NSG permite inbound puerto 1433 desde internet |
| Crítico | CustomScriptExtension |
Extension CustomScriptExtension instalada — puede ejecutar scripts arbitrarios |
| Crítico | RunCommandExtension |
Extension RunCommandWindows/Linux instalada — RCE directo |
| Crítico | SqlMixedAuthEnabled |
SQL VM con autenticación mixta (SQL auth habilitada, no solo Windows) |
| Crítico | SqlPublicConnectivity |
SQL VM con sqlConnectivity = "PUBLIC" — SQL Server accesible desde internet |
| Alto | OsDiskNotEncrypted |
Disco OS sin Customer-Managed Key (CMK) asignado |
| Alto | DataDiskNotEncrypted |
Algún disco de datos sin CMK |
| Alto | EncryptionAtHostDisabled |
Cifrado a nivel de hipervisor no habilitado (discos temporales sin cifrar) |
| Alto | SecureBootDisabled |
Secure Boot no habilitado (Trusted Launch no configurado) |
| Alto | VtpmDisabled |
Virtual TPM no habilitado |
| Alto | AadLoginNotConfigured |
Extension AAD Login no instalada — sin autenticación centralizada con MFA |
| Alto | NoManagedIdentity |
Sin identidad administrada — la VM usa credenciales almacenadas |
| Alto | BootDiagnosticsEnabled |
Boot diagnostics activo (escritura a storage account no cifrado con CMK) |
| Alto | SqlEolVersion |
SQL VM con versión EOL: SQL Server 2008, 2012 o 2014 |
| Alto | SqlNoBackup |
SQL VM sin configuración de backup automático |
| Alto | MmaInstalled |
Microsoft Monitoring Agent / OMS Agent instalado |
| Info | NoTags |
Sin tags — dificulta inventario y atribución |
| Info | IsSpotInstance |
VM de tipo Spot (puede ser eviccionada) |
| Info | EphemeralOsDisk |
Disco OS efímero (sin persistencia) |
| Info | SingleNic |
Una sola interfaz de red |
Objeto devuelto por VM (pipeline):
| Campo | Descripción |
|---|---|
SubscriptionId / SubscriptionName |
Suscripción |
ResourceGroup |
Resource group |
VmName, VmId, Location, OsType, VmSize |
Identidad de la VM |
OsImagePublisher, OsImageOffer, OsImageSku |
Imagen del OS |
PowerState, OsName |
Estado de encendido y nombre del OS (solo con -IncludeInstanceView) |
ProvisioningState |
Estado de aprovisionamiento |
HasPublicIp, RdpExposed, SshExposed, WinRmExposed, SqlPortExposed |
Checks críticos de red |
CustomScriptExtension, RunCommandExtension |
Checks críticos de extensiones |
SqlMixedAuthEnabled, SqlPublicConnectivity |
Checks críticos de SQL |
OsDiskNotEncrypted, DataDiskNotEncrypted, EncryptionAtHostDisabled |
Checks altos de cifrado |
SecureBootDisabled, VtpmDisabled |
Checks altos de Trusted Launch |
AadLoginNotConfigured, NoManagedIdentity, BootDiagnosticsEnabled, MmaInstalled |
Checks altos de identidad |
SqlEolVersion, SqlNoBackup |
Checks altos de SQL |
NoTags, IsSpotInstance, EphemeralOsDisk, SingleNic |
Checks informativos |
PublicIpAddresses |
IPs públicas asignadas (comma-separated) |
PrivateIpAddresses |
IPs privadas (comma-separated) |
NicCount |
Número de interfaces de red |
InstalledExtensions |
Tipos de extensiones instaladas (comma-separated) |
OpenInboundPorts |
Puertos abiertos desde internet (ej. "22,3389") o "unknown (no NSG)" |
ManagedIdentityType |
None, SystemAssigned, UserAssigned o Both |
DataDiskCount |
Número de discos de datos |
IsSqlVm |
$true si la VM tiene el SQL VM provider overlay |
SqlImageSku, SqlLicenseType, SqlConnectivity, SqlManagementMode |
Metadatos SQL (si IsSqlVm) |
RawFilePath |
Directorio del dump ($null si no se usó -OutputPath) |
Correlación VM → NIC → IP Pública → NSG:
La función resuelve automáticamente la cadena completa de recursos de red para cada VM. Por cada NIC en networkProfile.networkInterfaces:
- Recupera la NIC completa via ARM ID
- Sigue las referencias
publicIPAddress.idpara obtener las IPs públicas reales - Obtiene las reglas NSG asociadas a la NIC y evalúa puertos peligrosos
Si una VM tiene IP pública pero no tiene NSG asociada a ninguna NIC, OpenInboundPorts se marca como "unknown (no NSG)" para indicar que la exposición no pudo evaluarse (posiblemente solo protegida por Azure Firewall o sin protección).
Estructura de salida en disco (-OutputPath):
<OutputPath>/
VirtualMachinesRawDump/
<Suscripcion>/
<VmName>/
vm.json ← ARM object completo de la VM
extensions.json ← lista de extensiones instaladas
network.json ← NICs con IPs y NSGs
sqlvm.json ← SQL VM object (solo si IsSqlVm)
AzRA-VirtualMachines_<timestamp>.csv
# 1. Importar módulo y obtener token
Import-Module .\AzRA.psd1
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# 2. Escaneo completo con instance view
$vms = Get-AzRA-VirtualMachines -AccessToken $token -IncludeInstanceView -OutputPath 'C:\Audit'
Write-Output "VMs encontradas: $($vms.Count)"
Write-Output "SQL VMs: $(($vms | Where-Object { $_.IsSqlVm }).Count)"
# 3. Resumen de exposición de red
Write-Output "`n--- CRÍTICO: VMs con RDP/SSH abierto a internet ---"
$vms | Where-Object { $_.RdpExposed -or $_.SshExposed } |
Select-Object VmName, ResourceGroup, OsType, PublicIpAddresses, OpenInboundPorts | Format-Table
# 4. Extensiones peligrosas
Write-Output "`n--- CRÍTICO: VMs con extensiones de ejecución remota ---"
$vms | Where-Object { $_.CustomScriptExtension -or $_.RunCommandExtension } |
Select-Object VmName, ResourceGroup, InstalledExtensions | Format-Table
# 5. SQL VMs vulnerables
Write-Output "`n--- CRÍTICO: SQL VMs con conectividad pública o auth mixta ---"
$vms | Where-Object { $_.IsSqlVm -and ($_.SqlPublicConnectivity -or $_.SqlEolVersion) } |
Select-Object VmName, ResourceGroup, SqlImageSku, SqlConnectivity | Format-Table
# 6. VMs con mayor superficie de ataque
$vms |
Select-Object VmName, ResourceGroup,
@{N='CriticalCount'; E={
@($_.HasPublicIp, $_.RdpExposed, $_.SshExposed, $_.WinRmExposed,
$_.CustomScriptExtension, $_.RunCommandExtension) |
Where-Object { $_ -eq $true } | Measure-Object | Select-Object -Expand Count
}} |
Sort-Object CriticalCount -Descending | Format-TableAudita la configuración de seguridad del tenant de Entra ID (Azure Active Directory) desde perspectiva de pentester. A diferencia del resto de funciones, devuelve un único objeto por tenant con colecciones de findings organizadas por severidad. Cubre usuarios privilegiados, configuración de MFA, aplicaciones registradas, políticas de Conditional Access y ajustes globales de directorio.
Cómo funciona:
- Realiza las llamadas base con
Directory.Read.All:- Información del tenant (nombre, dominios, licencias P1/P2)
- Authorization Policy (quién puede registrar apps, invitar guests, dar consentimiento)
- Security Defaults
- Role definitions y role assignments (con expansión del principal en la misma llamada)
- Lista completa de usuarios +
signInActivitypara detectar cuentas inactivas - Service Principals para identificar apps con roles privilegiados
- Si
-IncludeMFAReport: lee/reports/authenticationMethods/userRegistrationDetailspara evaluar el estado de MFA de cada usuario - Si
-IncludeApps: enumera todas las App Registrations y evalúa credenciales expiradas, apps sin propietario, apps multi-tenant y permisos Graph sensibles - Si
-IncludeConditionalAccess: lee todas las CA policies y evalúa si la auth legacy está bloqueada, si se exige MFA a admins y usuarios, y si hay políticas en modo report-only - Cruza los datos entre colecciones (ej: Global Admins → MFA index) para findings combinados
- Exporta CSVs y dump JSON raw si se especifica
-OutputPath
Token necesario: Graph API — scope https://graph.microsoft.com/
Permisos por módulo:
| Switch | Permiso requerido | Qué analiza |
|---|---|---|
| (base, siempre) | Directory.Read.All |
Tenant, roles, usuarios, SPs, authorization policy |
-IncludeMFAReport |
Reports.Read.All + AuditLog.Read.All |
Estado MFA por usuario, admins sin MFA |
-IncludeApps |
Application.Read.All |
App registrations: credenciales, propietarios, permisos |
-IncludeConditionalAccess |
Policy.Read.All |
CA policies: legacy auth, MFA gaps, report-only |
Si alguna llamada devuelve 403, se emite Write-Warning con el permiso que falta y el análisis continúa con los datos disponibles.
$graphToken = (az account get-access-token --resource https://graph.microsoft.com | ConvertFrom-Json).accessToken
# Análisis base (solo Directory.Read.All)
Get-AzRA-EntraID -GraphToken $graphToken
# Análisis completo con todos los módulos
Get-AzRA-EntraID -GraphToken $graphToken -IncludeMFAReport -IncludeApps -IncludeConditionalAccess
# Con exportación a disco (CSVs + JSON raw dump)
Get-AzRA-EntraID -GraphToken $graphToken -IncludeMFAReport -IncludeApps -IncludeConditionalAccess -OutputPath 'C:\Audit'
# Solo una suscripción / tenant (el token ya define el scope del tenant)
# Ver Global Admins sin MFA (CRÍTICO)
$r = Get-AzRA-EntraID -GraphToken $graphToken -IncludeMFAReport
$r.GlobalAdminsWithoutMFA | Select-Object DisplayName, UserPrincipalName, MethodsRegistered
# Ver usuarios Guest con roles privilegiados (CRÍTICO)
$r.PrivilegedGuests | Select-Object DisplayName, UserPrincipalName, Roles
# Ver cuentas privilegiadas inactivas >90 días (ALTO)
$r.StalePrivilegedAccounts | Select-Object DisplayName, UserPrincipalName, LastSignIn, Roles
# Ver si legacy auth está bloqueada (CRÍTICO)
$r.LegacyAuthNotBlocked # $true = RIESGO (no está bloqueada)
# Ver resumen de misconfiguraciones críticas y altas
$r | Select-Object TenantDisplayName, HasCriticalFindings, HasHighFindings,
SecurityDefaultsDisabled, UsersCanRegisterApps, GuestInvitationNotRestricted,
LegacyAuthNotBlocked, NoMFARequiredForAdmins, MFARegistrationRate
# Ver apps con permisos Graph sensibles (CRÍTICO)
$r.AppsWithBroadPermissions | Select-Object DisplayName, AppId, SensitivePermissions
# Ver CA policies en modo report-only (no aplicadas)
$r.CAPoliciesReportOnly | Select-Object DisplayName, StateParámetros:
| Parámetro | Tipo | Descripción |
|---|---|---|
-GraphToken |
string |
Token Bearer de Microsoft Graph API (obligatorio) |
-OutputPath |
string |
Carpeta de salida para JSONs y CSVs (nombres auto-generados con timestamp) |
-IncludeMFAReport |
switch |
Activa el análisis de estado MFA por usuario (requiere Reports.Read.All) |
-IncludeApps |
switch |
Activa el análisis de App Registrations (requiere Application.Read.All) |
-IncludeConditionalAccess |
switch |
Activa el análisis de CA policies (requiere Policy.Read.All) |
-MaxRetries |
int |
Máximo de reintentos ante throttling/errores 5xx (1-10). Default: 3 |
-RetryDelaySec |
int |
Segundos base entre reintentos, multiplicado por el número de intento (1-60). Default: 5 |
Checks de seguridad evaluados:
| Severidad | Campo | Condición de riesgo |
|---|---|---|
| Crítico | GlobalAdminsWithoutMFA |
Global Administrators sin ningún método MFA registrado |
| Crítico | PrivilegedGuests |
Usuarios de tipo Guest con roles privilegiados asignados |
| Crítico | PrivilegedServicePrincipals |
Service Principals con roles privilegiados (Global Admin, Security Admin, etc.) |
| Crítico | LegacyAuthNotBlocked |
Sin CA policy activa que bloquee exchangeActiveSync / other (auth legacy) |
| Crítico | NoMFARequiredForAdmins |
Sin CA policy activa que exija MFA a roles privilegiados |
| Crítico | AppsWithBroadPermissions |
Apps con permisos sensibles admin-consented: Directory.ReadWrite.All, Mail.ReadWrite, Application.ReadWrite.All, etc. |
| Alto | SecurityDefaultsDisabled |
Security Defaults desactivadas — sin baseline de MFA/legacy auth |
| Alto | UsersCanRegisterApps |
allowedToCreateApps = true — cualquier usuario puede registrar aplicaciones |
| Alto | UsersCanConsentToApps |
Política de consent no restringida — usuarios pueden autorizar apps a acceder a sus datos |
| Alto | GuestInvitationNotRestricted |
allowInvitesFrom permite invitación por usuarios no-admin |
| Alto | AdminConsentWorkflowDisabled |
Sin flujo de aprobación para consent de apps — facilita illicit consent grant attacks |
| Alto | StalePrivilegedAccounts |
Cuentas con rol privilegiado sin actividad en >90 días |
| Alto | UsersWithoutMFA |
Usuarios sin ningún método MFA registrado |
| Alto | UsersWithWeakMFA |
Usuarios con MFA solo por SMS/voz (vulnerable a SIM swapping) |
| Alto | AppsWithExpiredCredentials |
Apps con passwordCredentials o keyCredentials expirados |
| Alto | AppsWithoutOwners |
Apps registradas sin propietario asignado |
| Alto | MultiTenantApps |
Apps con signInAudience externo al tenant — accesibles desde otros tenants |
| Alto | CAPoliciesReportOnly |
CA policies en modo reportOnly — reportan pero no aplican restricciones |
| Alto | NoCARequiringMFAForAllUsers |
Sin CA policy que exija MFA para todos los usuarios |
Objeto devuelto por tenant:
| Campo | Descripción |
|---|---|
TenantId, TenantDisplayName, TenantCreatedDateTime |
Identidad del tenant |
VerifiedDomains |
Array de dominios verificados |
TenantHasP1P2 |
$true si el tenant tiene licencia Azure AD Premium P1 o P2 |
HasCriticalFindings / HasHighFindings |
Flags de resumen para filtrado rápido |
TotalUserCount, GuestCount, GlobalAdminCount |
Estadísticas del tenant |
MFARegistrationRate |
% de usuarios con MFA registrado (solo con -IncludeMFAReport) |
ExternalAppsCount |
Número de apps multi-tenant (solo con -IncludeApps) |
ActiveCAPoliciesCount |
CA policies en estado enabled (solo con -IncludeConditionalAccess) |
GlobalAdminsWithoutMFA |
Array de {Id, DisplayName, UPN, MethodsRegistered} |
PrivilegedGuests |
Array de {Id, DisplayName, UPN, Roles} |
PrivilegedServicePrincipals |
Array de {Id, DisplayName, AppId, Roles} |
StalePrivilegedAccounts |
Array de {Id, DisplayName, UPN, LastSignIn, Roles} |
UsersWithoutMFA / UsersWithWeakMFA |
Arrays de usuarios |
AppsWithBroadPermissions |
Array de {DisplayName, AppId, SignInAudience, SensitivePermissions} |
AppsWithExpiredCredentials |
Array de {DisplayName, AppId, CredentialType, ExpiredOn} |
GlobalAdmins |
Lista completa de Global Administrators |
AllPrivilegedRoleAssignments |
Todos los role assignments a roles de alta privilegio |
CAPoliciesReportOnly |
Políticas activas pero no aplicadas |
Estructura de salida en disco (-OutputPath):
<OutputPath>/
EntraIDRawDump/
tenant.json ← /organization object
authorizationPolicy.json ← authorization policy
securityDefaults.json ← security defaults enforcement policy
roleAssignments.json ← todos los role assignments con principal
conditionalAccessPolicies.json ← CA policies (si -IncludeConditionalAccess)
applications.json ← app registrations (si -IncludeApps)
mfaRegistrationDetails.json ← MFA report (si -IncludeMFAReport)
AzRA-EntraID-Summary_<timestamp>.csv ← 1 fila con todos los flags y conteos
AzRA-EntraID-GlobalAdmins_<timestamp>.csv ← lista de Global Admins
AzRA-EntraID-PrivilegedUsers_<timestamp>.csv ← todos los role assignments privilegiados
AzRA-EntraID-MFA_<timestamp>.csv ← estado MFA de todos los usuarios
AzRA-EntraID-Apps_<timestamp>.csv ← apps con checks de seguridad
AzRA-EntraID-CAPolicies_<timestamp>.csv ← CA policies con evaluación
Permisos sensibles de Graph monitorizados (-IncludeApps):
| Permiso | Por qué es crítico |
|---|---|
Directory.ReadWrite.All |
Modificar cualquier objeto del directorio |
Directory.Read.All |
Leer todo el directorio (usuarios, grupos, apps, roles) |
User.ReadWrite.All |
Modificar cualquier usuario del tenant |
Mail.ReadWrite / Mail.Read |
Leer y escribir todos los buzones de correo |
Group.ReadWrite.All |
Gestionar todos los grupos (incluyendo roles) |
Application.ReadWrite.All |
Crear y modificar aplicaciones — permite backdoors |
AppRoleAssignment.ReadWrite.All |
Asignar roles de aplicación — escalada de privilegios |
RoleManagement.ReadWrite.Directory |
Gestionar roles de directorio — acceso total |
# 1. Importar módulo y obtener token
Import-Module .\AzRA.psd1
$graphToken = (az account get-access-token --resource https://graph.microsoft.com | ConvertFrom-Json).accessToken
# 2. Análisis completo
$r = Get-AzRA-EntraID -GraphToken $graphToken `
-IncludeMFAReport -IncludeApps -IncludeConditionalAccess `
-OutputPath 'C:\Audit' -Verbose
# 3. Resumen ejecutivo
Write-Output "Tenant: $($r.TenantDisplayName) ($($r.TenantId))"
Write-Output "Licencia P1/P2: $($r.TenantHasP1P2)"
Write-Output "Usuarios totales: $($r.TotalUserCount) | Guests: $($r.GuestCount)"
Write-Output "Global Admins: $($r.GlobalAdminCount)"
Write-Output "MFA registration rate: $($r.MFARegistrationRate)%"
# 4. Findings críticos
Write-Output "`n=== CRÍTICOS ==="
if ($r.GlobalAdminsWithoutMFA) {
Write-Output "[!] Global Admins sin MFA:"
$r.GlobalAdminsWithoutMFA | ForEach-Object { Write-Output " $($_.UserPrincipalName)" }
}
if ($r.PrivilegedGuests) {
Write-Output "[!] Guest users con roles privilegiados:"
$r.PrivilegedGuests | ForEach-Object { Write-Output " $($_.UserPrincipalName) → $($_.Roles)" }
}
if ($r.LegacyAuthNotBlocked) { Write-Output "[!] Legacy authentication NO está bloqueada" }
if ($r.NoMFARequiredForAdmins) { Write-Output "[!] No hay CA policy que exija MFA a admins" }
if ($r.AppsWithBroadPermissions) {
Write-Output "[!] Apps con permisos Graph sensibles:"
$r.AppsWithBroadPermissions | ForEach-Object {
Write-Output " $($_.DisplayName) → $($_.SensitivePermissions)"
}
}
# 5. Findings altos
Write-Output "`n=== ALTOS ==="
if ($r.SecurityDefaultsDisabled) { Write-Output "[-] Security Defaults desactivadas" }
if ($r.UsersCanRegisterApps) { Write-Output "[-] Los usuarios pueden registrar aplicaciones" }
if ($r.GuestInvitationNotRestricted) { Write-Output "[-] Invitación de guests no restringida a admins" }
if ($r.StalePrivilegedAccounts) {
Write-Output "[-] Cuentas privilegiadas sin actividad >90 días:"
$r.StalePrivilegedAccounts | ForEach-Object {
Write-Output " $($_.UserPrincipalName) — último acceso: $($_.LastSignIn ?? 'nunca')"
}
}
Write-Output "[-] Usuarios sin MFA: $($r.UsersWithoutMFA.Count)"
Write-Output "[-] Usuarios con MFA débil (SMS/voz): $($r.UsersWithWeakMFA.Count)"
# 6. Apps problemáticas
if ($r.AppsWithExpiredCredentials) {
Write-Output "`n[-] Apps con credenciales expiradas:"
$r.AppsWithExpiredCredentials | Select-Object DisplayName, CredentialType, ExpiredOn | Format-Table
}
if ($r.AppsWithoutOwners) {
Write-Output "[-] Apps sin propietario: $($r.AppsWithoutOwners.Count)"
}Enumera todos los Azure Container Registries (ACR) accesibles, evalúa misconfiguraciones de seguridad a nivel ARM, y opcionalmente realiza reconocimiento del data plane (repositorios, tags, tamaños de imagen) con selección interactiva para docker pull.
Cómo funciona:
La función opera en dos capas diferenciadas:
Capa ARM (token management.azure.com, siempre activa):
- Lista todos los registries en las subscripciones accesibles via
Microsoft.ContainerRegistry/registries - Evalúa checks críticos, altos e informativos sobre la configuración del registry
- Intenta obtener credenciales de admin si
adminUserEnabledestá activo - Recupera la configuración de Diagnostic Settings para verificar si se registran logs
Capa data plane (-ScanRepositories):
- Intercambia el ARM token por un ACR refresh token via
POST https://{registry}.azurecr.io/oauth2/exchange— equivalente aaz acr login --expose-token - Obtiene access tokens con scope específico (
registry:catalog:*,repository:{repo}:pull) - Enumera repositorios (
/v2/_catalog, paginado via headerLink) - Para cada repositorio lista tags y calcula el tamaño comprimido de cada imagen sumando
config.size + layers[].sizedel manifest v2 - Genera automáticamente un archivo
.txtcon todos los comandosdocker pull
Selección interactiva (-InteractivePull):
- Muestra tabla con índice, imagen completa y tamaño antes de ejecutar ningún pull
- El usuario selecciona por índice (ej:
1,3),allonone - Si el total supera 5 GB, pide confirmación adicional
- Si Docker no está disponible, genera igualmente el archivo de comandos
Checks de seguridad (ARM):
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | AnonymousPullEnabled |
Cualquier usuario puede hacer pull sin autenticación |
| Crítico | AdminUserEnabled |
Usuario admin habilitado (credenciales estáticas) |
| Crítico | PublicNetworkAccessEnabled |
Acceso público sin restricciones de red |
| Alto | NoFirewallRules |
Sin reglas de firewall IP configuradas |
| Alto | RetentionPolicyDisabled |
Sin política de retención de imágenes no etiquetadas |
| Alto | ContentTrustDisabled |
Content Trust (firma de imágenes) no habilitado |
| Alto | DiagnosticLogsDisabled |
Sin Diagnostic Settings configurados |
| Alto | BasicSku |
SKU Basic no soporta content trust, private endpoints ni geo-replicación |
| Info | NoPrivateEndpoints |
Sin Private Endpoints configurados |
| Info | ZoneRedundancyDisabled |
Sin redundancia de zona habilitada |
Permisos necesarios:
Microsoft.ContainerRegistry/registries/read— baseMicrosoft.ContainerRegistry/registries/listCredentials/action— solo si admin user habilitadomicrosoft.insights/diagnosticSettings/read— opcional, para check de logs
Uso básico:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Solo checks de seguridad ARM
Get-AzRA-ContainerRegistries -AccessToken $token
# Una sola subscripción
Get-AzRA-ContainerRegistries -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'Enumeración de repositorios y generación de pull commands:
# Enumera repos/tags/tamaños y genera AzRA-ACR-PullCommands_<timestamp>.txt
Get-AzRA-ContainerRegistries -AccessToken $token -ScanRepositories -OutputPath 'C:\Audit'Selección interactiva de imágenes:
# Muestra tabla, pregunta qué hacer pull, ejecuta docker pull para los seleccionados
Get-AzRA-ContainerRegistries -AccessToken $token -ScanRepositories -InteractivePullEjemplo de salida interactiva:
[*] Seleccion interactiva de imagenes para docker pull
Registry: miacr.azurecr.io (3 imagenes, 2.4 GB total)
Idx Imagen Tamano
--- ------ ------
[1] hasura/graphql-engine:v2.38.1 1.2 GB
[2] myapp/backend:latest 800 MB
[3] myapp/frontend:v1.0 400 MB
Introduce los indices a descargar (ej: 1,3), 'all' para todos, 'none' para omitir:
> 1,2
[~] Ejecutando: docker pull miacr.azurecr.io/hasura/graphql-engine:v2.38.1
[~] Ejecutando: docker pull miacr.azurecr.io/myapp/backend:latest
Filtrado de resultados:
$result = Get-AzRA-ContainerRegistries -AccessToken $token -ScanRepositories
# Registries con admin user habilitado o pull anónimo
$result | Where-Object { $_.AdminUserEnabled -or $_.AnonymousPullEnabled }
# Registries donde se obtuvo acceso al data plane
$result | Where-Object { $_.DataPlaneAccessible -eq $true } |
Select-Object RegistryName, RepositoryCount, TotalImageSizeGB
# Ver detalle de repos de un registry
$result | Where-Object { $_.RegistryName -eq 'miacr' } |
ForEach-Object {
$_.Repositories | Format-Table RepositoryName, TagCount,
@{N='SizeMB'; E={[Math]::Round($_.TotalSizeBytes/1MB, 1)}}
}
# Solo registries con findings críticos
$result | Where-Object { $_.HasCriticalFindings } |
Select-Object LoginServer, AnonymousPullEnabled, AdminUserEnabled, PublicNetworkAccessEnabledFlujo de reconocimiento (replica el proceso manual):
# Equivalente a:
# az acr list
# az acr login --expose-token --name $server
# docker login $server -u 00000000-0000-0000-0000-000000000000 -p $token
# az acr repository list --name $server
# az acr repository show-tags --name $server --repository "hasura/graphql-engine"
# docker pull $server/hasura/graphql-engine:v2.38.1
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
$result = Get-AzRA-ContainerRegistries -AccessToken $token -ScanRepositories -InteractivePull -OutputPath 'C:\Audit'Archivos generados (-OutputPath):
C:\Audit\
AzRA-ContainerRegistries_<timestamp>.csv # 1 fila por registry, todos los checks booleanos
AzRA-ContainerRegistries-Repos_<timestamp>.csv # 1 fila por imagen (registry, repo, tag, tamano)
AzRA-ACR-PullCommands_<timestamp>.txt # Comandos docker pull para todas las imagenes
ContainerRegistriesRawDump\
<SubscriptionName>\
<RegistryName>\
registry.json # Objeto ARM completo del registry
diagnostics.json # Estado de Diagnostic Settings
repositories.json # Repos, tags y tamanios (si -ScanRepositories)
Enumera todas las Function Apps y App Services, evalúa misconfiguraciones de seguridad a nivel ARM y opcionalmente extrae todas las app settings en texto claro. Es uno de los vectores más rentables en auditorías Azure: las app settings contienen habitualmente connection strings, storage account keys, client secrets de Entra ID, API keys de terceros y tokens hardcodeados.
Cómo funciona:
Checks ARM (siempre activos):
- Recupera todas las apps via
Microsoft.Web/sites(incluye Function Apps, Web Apps y API Apps) - Para cada app obtiene la configuración de sitio (
/config/web) y las auth settings V2 (/config/authsettingsV2) - Evalúa restricciones IP (
ipSecurityRestrictions) y acceso SCM/Kudu (scmIpSecurityRestrictions) - Detecta managed identities y VNet integration
Extracción de app settings (-ScanSecrets):
- Llama a
POST /config/appsettings/listpor cada app — endpoint ARM que devuelve todos los pares clave/valor en texto claro - Con
-IncludeSlotsrepite la extracción en cada slot de staging (producción y staging frecuentemente comparten las mismas secrets con menor restricción de red)
Checks de seguridad:
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | HttpsOnlyDisabled |
httpsOnly != true — permite tráfico HTTP sin cifrar |
| Crítico | AuthDisabled + NoIpRestrictions |
App pública sin autenticación ni restricciones de red |
| Crítico | PublicScmAccess |
Consola Kudu accesible públicamente sin restricciones (potencial RCE con publish profile) |
| Crítico | HasSecrets |
Se extrajeron app settings en texto claro (-ScanSecrets) |
| Alto | MinTlsWeak |
TLS mínimo 1.0 o 1.1 |
| Alto | FtpEnabled |
FTP no deshabilitado (ftpsState no es Disabled) |
| Alto | RemoteDebuggingEnabled |
Depuración remota activa |
| Alto | AlwaysOnDisabled |
Function App sin Always On (cold starts, puede evadir monitoreo) |
| Alto | HasManagedIdentity + NoVNet |
Managed Identity sin VNet integration (acceso directo a internet) |
| Info | SlotCount |
Número de slots de staging existentes |
| Info | VNetIntegrated |
Si la app tiene VNet integration |
Permisos necesarios:
Microsoft.Web/sites/read— baseMicrosoft.Web/sites/config/list/action— para-ScanSecretsMicrosoft.Web/sites/slots/read— para-IncludeSlots
Uso básico:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Solo checks de seguridad ARM
Get-AzRA-FunctionApps -AccessToken $token
# Extracción de app settings (el vector principal)
Get-AzRA-FunctionApps -AccessToken $token -ScanSecrets -OutputPath 'C:\Audit'
# Con slots de staging
Get-AzRA-FunctionApps -AccessToken $token -ScanSecrets -IncludeSlots -OutputPath 'C:\Audit'Filtrado de resultados:
$result = Get-AzRA-FunctionApps -AccessToken $token -ScanSecrets
# Mostrar todas las secrets extraidas
$result | Where-Object { $_.HasSecrets } | ForEach-Object {
Write-Output "=== $($_.AppName) ==="
$_.Secrets | ForEach-Object { Write-Output " $($_.Name) = $($_.Value)" }
}
# Apps con Kudu público
$result | Where-Object { $_.PublicScmAccess } |
Select-Object AppName, ResourceGroup, DefaultHostName
# Solo Function Apps con auth deshabilitada
$result | Where-Object { $_.IsFunctionApp -and $_.AuthDisabled } |
Select-Object AppName, DefaultHostName, NoIpRestrictions
# Apps con managed identity (mapeo de posibles rutas de escalada)
$result | Where-Object { $_.HasManagedIdentity } |
Select-Object AppName, ManagedIdentityType, ManagedIdentityPrincipalId, VNetIntegratedArchivos generados (-OutputPath):
C:\Audit\
AzRA-FunctionApps_<timestamp>.csv # 1 fila por app, todos los checks booleanos
AzRA-FunctionApps-Secrets_<timestamp>.csv # 1 fila por app setting extraida (si -ScanSecrets)
FunctionAppsRawDump\
<SubscriptionName>\
<AppName>\
app.json # Objeto ARM completo
appsettings.json # App settings raw (si -ScanSecrets)
slots.json # Lista de slots (si -IncludeSlots)
Enumera todos los servicios de Azure API Management, evalúa misconfiguraciones de seguridad y opcionalmente extrae tres categorías de secretos: subscription keys, named values de tipo secret y credenciales de backends. APIM actúa como gateway centralizado y frecuentemente almacena credenciales de todos los servicios backend a los que llama.
Cómo funciona:
Checks ARM (siempre activos):
- Lista servicios via
Microsoft.ApiManagement/service - Evalúa
virtualNetworkType(None/External/Internal) — sin VNet = exposición pública completa - Lee
customPropertiespara detectar TLS 1.0/1.1, SSL 3.0 y cifrado 3DES - Comprueba Diagnostic Settings para verificar si se registran logs
- El endpoint de gestión directa (puerto 3443) queda expuesto cuando no hay VNet integration
Extracción de secretos (-ScanSecrets), en orden de valor ofensivo:
- Named Values secretos: enumera todos los Named Values con
secret=truey llama alistValuepor cada uno — contienen API keys, tokens y passwords usados en políticas del gateway - Subscription Keys: lista todas las suscripciones APIM y llama a
listSecretspara obtenerprimaryKeyysecondaryKey— permiten llamar a las APIs publicadas - Backend credentials: enumera todos los backends y extrae credenciales de
authorization(basic auth) y headers personalizados (frecuentementeAuthorization: Bearer <token>)
Checks de seguridad:
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | PublicNetworkEnabled |
virtualNetworkType = None — gateway expuesto sin VNet |
| Crítico | DirectMgmtEndpointOpen |
Puerto 3443 accesible desde internet (sin VNet) |
| Crítico | DeveloperPortalEnabled |
Developer portal accesible públicamente |
| Crítico | HasSecrets |
Se extrajeron secretos (-ScanSecrets) |
| Alto | LegacyProtocolsEnabled |
TLS 1.0, TLS 1.1 o SSL 3.0 habilitados |
| Alto | WeakCiphersEnabled |
Cifrado 3DES habilitado |
| Alto | SkuConsumption |
SKU Consumption sin soporte para VNet integration |
| Alto | DiagnosticLogsDisabled |
Sin Diagnostic Settings configurados |
| Info | HasCustomDomains |
Dominios personalizados configurados |
| Info | VirtualNetworkType |
Tipo de integración de red (None/External/Internal) |
Permisos necesarios:
Microsoft.ApiManagement/service/read— baseMicrosoft.ApiManagement/service/subscriptions/listSecrets/action— subscription keysMicrosoft.ApiManagement/service/namedValues/listValue/action— named value secretsmicrosoft.insights/diagnosticSettings/read— opcional
Uso básico:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Solo checks de seguridad ARM
Get-AzRA-APIManagement -AccessToken $token
# Extracción completa de secretos
Get-AzRA-APIManagement -AccessToken $token -ScanSecrets -OutputPath 'C:\Audit'Filtrado de resultados:
$result = Get-AzRA-APIManagement -AccessToken $token -ScanSecrets
# Ver todos los secretos extraidos por tipo
$result | Where-Object { $_.HasSecrets } | ForEach-Object {
Write-Output "=== $($_.ServiceName) - $($_.SecretCount) secretos ==="
$_.Secrets | Format-Table SecretType, Name, Value
}
# Solo named values (API keys de terceros)
$result | ForEach-Object { $_.Secrets } |
Where-Object { $_.SecretType -eq 'NamedValue' } |
Format-Table ServiceName, Name, Value
# Subscription keys (para llamar a las APIs publicadas)
$result | ForEach-Object { $_.Secrets } |
Where-Object { $_.SecretType -eq 'SubscriptionKey' } |
Format-Table ServiceName, Name, Value
# Servicios con acceso público
$result | Where-Object { $_.PublicNetworkEnabled } |
Select-Object ServiceName, GatewayUrl, ManagementApiUrl, DeveloperPortalUrl, SkuArchivos generados (-OutputPath):
C:\Audit\
AzRA-APIManagement_<timestamp>.csv # 1 fila por servicio, checks booleanos
AzRA-APIManagement-Secrets_<timestamp>.csv # 1 fila por secreto extraido (si -ScanSecrets)
APIManagementRawDump\
<SubscriptionName>\
<ServiceName>\
service.json # Objeto ARM completo del servicio
namedValues.json # Lista de Named Values (sin valores de secretos)
subscriptions.json # Lista de suscripciones APIM
backends.json # Definiciones de backends con credenciales
Enumera todas las cuentas de Azure Cosmos DB, evalúa misconfiguraciones de seguridad y opcionalmente extrae las master keys. Las master keys dan acceso de lectura/escritura completo a todos los datos de la cuenta sin necesidad de pasar por RBAC de Azure.
Cómo funciona:
Checks ARM (siempre activos):
publicNetworkAccess: si el acceso público no está explícitamente deshabilitadoipRules+virtualNetworkRules: si no hay restricciones de red configuradasdisableLocalAuth: si las master keys están activas (valorfalse= keys disponibles)keyVaultKeyUri: si el cifrado usa claves de Microsoft en lugar de CMK- Diagnostic Settings para verificar logs de auditoría
Extracción de keys (-ScanSecrets):
- Llama a
POST .../listKeysy extraeprimaryMasterKey,secondaryMasterKey,primaryReadonlyMasterKeyysecondaryReadonlyMasterKey - Si
disableLocalAuth = true, avisa que solo RBAC está disponible y omite la llamada
Checks de seguridad:
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | NoFirewallRules |
Acceso público sin restricciones IP ni VNet |
| Crítico | SinPrivateEndpoint |
Cuenta pública sin private endpoint configurado |
| Crítico | HasKeys |
Master keys extraídas (acceso total a todos los datos) |
| Alto | LocalAuthEnabled |
disableLocalAuth = false — keys activas |
| Alto | CmkNotConfigured |
Cifrado con claves de Microsoft, no del cliente |
| Alto | DiagnosticLogsDisabled |
Sin Diagnostic Settings configurados |
Permisos necesarios:
Microsoft.DocumentDB/databaseAccounts/read— baseMicrosoft.DocumentDB/databaseAccounts/listKeys/action— para-ScanSecrets
Uso:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Checks de seguridad ARM
Get-AzRA-CosmosDB -AccessToken $token
# Con extraccion de master keys
Get-AzRA-CosmosDB -AccessToken $token -ScanSecrets -OutputPath 'C:\Audit'Filtrado de resultados:
$result = Get-AzRA-CosmosDB -AccessToken $token -ScanSecrets
# Accounts con acceso publico sin firewall
$result | Where-Object { $_.NoFirewallRules } |
Select-Object AccountName, DocumentEndpoint, LocalAuthEnabled
# Ver keys extraidas
$result | Where-Object { $_.HasKeys } | ForEach-Object {
Write-Output "=== $($_.AccountName) ==="
$_.Keys | Format-Table KeyName, KeyValue, DocumentEndpoint
}
# Conexion directa con la key extraida (ejemplo)
# $ctx = New-AzCosmosDBContext -Endpoint $result[0].DocumentEndpoint -Key $result[0].Keys[0].KeyValueArchivos generados (-OutputPath):
C:\Audit\
AzRA-CosmosDB_<timestamp>.csv # 1 fila por account, todos los checks
AzRA-CosmosDB-Keys_<timestamp>.csv # 1 fila por key extraida (si -ScanSecrets)
CosmosDBRawDump\
<SubscriptionName>\
<AccountName>\
account.json # Objeto ARM completo
keys.json # Keys raw (si -ScanSecrets)
diagnostics.json # Estado de Diagnostic Settings
Enumera todos los namespaces de Azure Event Hubs, evalúa firewall y configuración de TLS, y opcionalmente extrae las SAS keys (Shared Access Signatures) de todas las reglas de autorización a nivel namespace y por Event Hub individual. Las SAS keys con permiso Manage equivalen a acceso de administrador completo.
Cómo funciona:
Checks ARM (siempre activos):
- Lee el
networkRuleSets/defaultdel namespace para obtenerdefaultAction,ipRulesyvirtualNetworkRules - Verifica
disableLocalAuth(SAS keys activas o deshabilitadas) - Comprueba
minimumTlsVersionpara detectar TLS 1.0/1.1 - Verifica SKU: Basic/Standard no soportan private endpoints ni VNet rules completas
- Recupera Diagnostic Settings
Extracción de SAS keys (-ScanSecrets):
- Enumera auth rules a nivel namespace y llama a
listkeyspor cada una - Enumera todos los Event Hubs del namespace y repite para sus auth rules individuales
- Marca como
IsManage = truelas reglas con permisoManage— permiten leer, escribir y administrar el namespace
Checks de seguridad:
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | NoFirewallRules |
Acceso público sin restricciones IP ni VNet (defaultAction = Allow) |
| Crítico | ManageKeyFound |
SAS key con permiso Manage encontrada — acceso de admin completo |
| Alto | MinTlsWeak |
TLS mínimo 1.0 o 1.1 |
| Alto | BasicOrStandardSku |
SKU Basic/Standard sin soporte completo para VNet ni private endpoints |
| Alto | LocalAuthEnabled |
disableLocalAuth = false — SAS keys activas |
| Alto | DiagnosticLogsDisabled |
Sin Diagnostic Settings configurados |
| Info | ZoneRedundant |
Si la redundancia de zona está habilitada |
Permisos necesarios:
Microsoft.EventHub/namespaces/read— baseMicrosoft.EventHub/namespaces/authorizationRules/listkeys/action— para-ScanSecrets(namespace level)Microsoft.EventHub/namespaces/eventhubs/authorizationRules/listkeys/action— para-ScanSecrets(Event Hub level)
Uso:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Checks de seguridad ARM
Get-AzRA-EventHubs -AccessToken $token
# Con extraccion de SAS keys
Get-AzRA-EventHubs -AccessToken $token -ScanSecrets -OutputPath 'C:\Audit'Filtrado de resultados:
$result = Get-AzRA-EventHubs -AccessToken $token -ScanSecrets
# Namespaces con Manage key (maxima severidad)
$result | Where-Object { $_.ManageKeyFound } |
Select-Object NamespaceName, ServiceBusEndpoint
# Ver todas las keys extraidas con sus permisos
$result | ForEach-Object { $_.Keys } |
Select-Object NamespaceName, Scope, RuleName, Rights, IsManage, KeyType, Value |
Sort-Object IsManage -Descending | Format-Table
# Solo connection strings (para uso directo)
$result | ForEach-Object { $_.Keys } |
Where-Object { $_.KeyType -match 'ConnectionString' } |
Select-Object NamespaceName, Scope, Rights, ValueArchivos generados (-OutputPath):
C:\Audit\
AzRA-EventHubs_<timestamp>.csv # 1 fila por namespace, todos los checks
AzRA-EventHubs-Keys_<timestamp>.csv # 1 fila por key extraida con scope y permisos
EventHubsRawDump\
<SubscriptionName>\
<NamespaceName>\
namespace.json # Objeto ARM completo del namespace
authRules.json # Auth rules nivel namespace
Enumera todas las User-Assigned Managed Identities (UAMIs) y resuelve sus role assignments en Azure ARM, identificando rutas de escalada de privilegios. Una identidad con rol Contributor o Owner a nivel subscripción permite a cualquier recurso que la use ejecutar cualquier operación en todos los recursos de la subscripción.
Cómo funciona:
La función opera en dos modos:
User-Assigned MIs (siempre):
- Lista todas las UAMIs via
Microsoft.ManagedIdentity/userAssignedIdentities - Para cada identidad usa su
principalIdpara consultar todos los role assignments en la subscripción via$filter=principalId eq '{id}' - Resuelve el nombre del rol desde la definición ARM (con caché para minimizar llamadas)
- Clasifica cada role assignment por severidad según rol y scope
System-Assigned MIs (-IncludeSystemAssigned):
- Enumera recursos de VMs, Function Apps, Logic Apps y Automation Accounts
- Extrae el
principalIdde los que tienenidentity.typeconteniendoSystemAssigned - Resuelve y clasifica sus role assignments con la misma lógica
Clasificación de roles:
| Severidad | Rol | Condición |
|---|---|---|
| Crítico | Owner |
A nivel subscription o resource group |
| Crítico | Contributor |
A nivel subscription |
| Crítico | UserAccessAdministrator |
En cualquier scope (puede asignar cualquier rol) |
| Alto | Contributor |
A nivel resource group |
| Alto | SecurityAdmin, KeyVaultContributor, WebsiteContributor |
En cualquier scope |
| Alto | StorageAccountContributor, LogicAppContributor, AutomationContributor |
En cualquier scope |
Permisos necesarios:
Microsoft.ManagedIdentity/userAssignedIdentities/readMicrosoft.Authorization/roleAssignments/readMicrosoft.Authorization/roleDefinitions/read
Uso:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Solo User-Assigned MIs
Get-AzRA-ManagedIdentities -AccessToken $token
# Con System-Assigned MIs y exportacion
Get-AzRA-ManagedIdentities -AccessToken $token -IncludeSystemAssigned -OutputPath 'C:\Audit'Filtrado de resultados:
$result = Get-AzRA-ManagedIdentities -AccessToken $token -IncludeSystemAssigned
# Identidades con rutas de escalada criticas
$result | Where-Object { $_.HasCriticalFindings } | ForEach-Object {
Write-Output "=== [$($_.IdentityType)] $($_.IdentityName) ==="
$_.RoleAssignments | Where-Object { $_.IsCritical } |
Format-Table RoleName, ScopeType, Scope
}
# Mapa completo de roles por identidad
$result | ForEach-Object {
[PSCustomObject]@{
Identity = $_.IdentityName
Type = $_.IdentityType
Roles = ($_.RoleAssignments | ForEach-Object { "$($_.RoleName)@$($_.ScopeType)" }) -join ' | '
Critical = $_.HasCriticalFindings
}
} | Format-Table -AutoSize
# Identidades con Owner o Contributor a nivel subscripcion (maxima prioridad)
$result | Where-Object { $_.HasCriticalFindings } |
Select-Object IdentityName, IdentityType, RoleCount |
Format-TableArchivos generados (-OutputPath):
C:\Audit\
AzRA-ManagedIdentities_<timestamp>.csv # 1 fila por identidad
AzRA-ManagedIdentities-RoleAssignments_<timestamp>.csv # 1 fila por role assignment
ManagedIdentitiesRawDump\
<SubscriptionName>\
<IdentityName>\
identity.json # Objeto ARM completo de la UAMI
roleAssignments.json # Lista de roles resueltos con severidad
Enumera todos los recursos de Azure Cognitive Services (proveedor Microsoft.CognitiveServices/accounts) y Azure AI Search (proveedor Microsoft.Search/searchServices) en todas las suscripciones accesibles. Para cada recurso evalúa la configuración de seguridad y, con -ScanSecrets, extrae las API keys en texto claro.
Recursos cubiertos:
- Azure OpenAI (kind:
OpenAI) - Azure AI Search (
Microsoft.Search/searchServices) - Speech Service (kind:
SpeechServices) - Language / Text Analytics (kind:
TextAnalytics) - Document Intelligence / Form Recognizer (kind:
FormRecognizer) - Content Safety (kind:
ContentSafety) - Computer Vision (kind:
ComputerVision) - Azure AI services (kind:
CognitiveServices) - Bing Search (kind:
Bing.Search.v7)
Checks de seguridad:
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | NoFirewallRules | Acceso público sin restricciones de IP ni VNet |
| Crítico | ApiKeysFound | API keys extraídas via listKeys/listAdminKeys |
| Alto | NoPrivateEndpoint | Sin private endpoint configurado |
| Alto | LocalAuthEnabled | Keys activas (disableLocalAuth=false) |
| Alto | OutboundNotRestricted | El servicio puede hacer llamadas salientes sin restricción |
| Alto | DiagnosticLogsDisabled | Sin Diagnostic Settings configurados |
| Alto | NoCustomerManagedKey | Cifrado con clave de Microsoft, no del cliente |
Extracción de keys:
- Cognitive Services: POST
listKeys→key1,key2 - AI Search: POST
listAdminKeys→primaryKey,secondaryKey - Si
disableLocalAuth=true, las keys están deshabilitadas (solo RBAC) y no se intentan extraer
Permisos requeridos:
Microsoft.CognitiveServices/accounts/read
Microsoft.CognitiveServices/accounts/listKeys/action (solo -ScanSecrets)
Microsoft.Search/searchServices/read
Microsoft.Search/searchServices/listAdminKeys/action (solo -ScanSecrets)
microsoft.insights/diagnosticSettings/read (opcional)
Uso básico:
$token = (az account get-access-token --resource https://management.azure.com | ConvertFrom-Json).accessToken
# Solo auditoría de configuración
Get-AzRA-CognitiveServices -AccessToken $token
# Con extracción de keys
Get-AzRA-CognitiveServices -AccessToken $token -ScanSecrets -OutputPath 'C:\Audit'
# Suscripción específica
Get-AzRA-CognitiveServices -AccessToken $token -SubscriptionId 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' -ScanSecretsInspeccionar keys extraídas:
$result = Get-AzRA-CognitiveServices -AccessToken $token -ScanSecrets
$result | Where-Object { $_.HasKeys } | ForEach-Object {
Write-Output "=== $($_.AccountName) [$($_.Kind)] ==="
$_.Keys | Format-Table KeyName, KeyValue
}Salida CSV generada:
AzRA-CognitiveServices_<timestamp>.csv # 1 fila por servicio
AzRA-CognitiveServices-Keys_<timestamp>.csv # 1 fila por key (solo con -ScanSecrets)
CognitiveServicesRawDump\
<SubscriptionName>\
<AccountName>\
account.json # Objeto ARM completo
keys.json # Keys extraídas (si -ScanSecrets)
Enumera organizaciones, proyectos, service connections, variable groups, agent pools y repositorios de Azure DevOps via la REST API (dev.azure.com). Identifica configuraciones de alto valor ofensivo como conexiones SPN a Azure, secretos en pipelines y agentes self-hosted como vectores de movimiento lateral.
Recursos enumerados:
- Organizations accesibles (via
app.vssps.visualstudio.com) - Proyectos en cada organización (visibilidad pública/privada)
- Service connections por proyecto (clasificados por tipo y esquema de auth)
- Variable groups por proyecto (con conteo de secretos y referencias a Key Vault)
- Agent pools org-level (self-hosted vs. Microsoft-hosted)
- Repositorios Git (inventario con URLs)
Checks de seguridad:
| Severidad | Check | Descripción |
|---|---|---|
| Crítico | AzureRM-SPN | Service connection de tipo azurerm con scheme ServicePrincipal |
| Crítico | KeyVaultLinkedGroup | Variable group de tipo AzureKeyVault (revela qué secretos del KV usa el pipeline) |
| Alto | ExternalServiceConnection | Cualquier conexión a GitHub, AWS, Docker Registry, HTTP genérico |
| Alto | SecretVariables | Variable group con variables marcadas como secretas (isSecret=true) |
| Alto | SelfHostedPool | Agent pool self-hosted (pivoting, SSRF, acceso a red interna) |
| Alto | PublicProject | Proyecto con visibilidad pública (accesible sin autenticación) |
Tokens de acceso:
# Azure AD Bearer token para DevOps (recomendado)
$devopsToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 | ConvertFrom-Json).accessToken
# Personal Access Token (PAT) - usar con -TokenIsPAT
# Scopes necesarios: vso.project, vso.serviceendpoint, vso.variablegroups_read, vso.code, vso.agentpoolsUso básico:
# Enumerar todo (descubrimiento automático de orgs)
Get-AzRA-AzureDevOps -AccessToken $devopsToken
# Con extracción de parámetros de auth en service connections
Get-AzRA-AzureDevOps -AccessToken $devopsToken -ScanSecrets -OutputPath 'C:\Audit'
# Organización específica con PAT
Get-AzRA-AzureDevOps -AccessToken 'mypatvalue' -TokenIsPAT -Organization 'myorg' -ScanSecrets
# Proyecto específico
Get-AzRA-AzureDevOps -AccessToken $devopsToken -Organization 'myorg' -Project 'myproject' -ScanSecretsInspeccionar resultados:
$result = Get-AzRA-AzureDevOps -AccessToken $devopsToken -ScanSecrets
# Service connections críticas con sus parámetros
$result.ServiceConnections | Where-Object { $_.HasCriticalFindings } |
Format-Table Organization, Project, ConnectionName, Scheme, AuthParams
# Variable groups con secretos
$result.VariableGroups | Where-Object { $_.HasCriticalFindings -or $_.HasHighFindings } |
Format-Table Organization, Project, GroupName, KeyVaultName, SecretCount
# Agentes self-hosted
$result.AgentPools | Where-Object { $_.IsSelfHosted } |
Format-Table Organization, PoolName, AgentCountSalida CSV generada:
AzRA-AzDevOps-ServiceConnections_<timestamp>.csv # 1 fila por service connection
AzRA-AzDevOps-VariableGroups_<timestamp>.csv # 1 fila por variable group
AzRA-AzDevOps-AgentPools_<timestamp>.csv # 1 fila por agent pool
AzRA-AzDevOps-Repos_<timestamp>.csv # 1 fila por repositorio
AzureDevOpsRawDump\
<OrgName>\
<ProjectName>\
serviceConnections.json # Lista completa de service connections
variableGroups.json # Lista completa de variable groups
Invoke-O365EmailValidator- Validar existencia de emails en Office 365
Valida direcciones de correo electrónico verificando su existencia en Office 365 sin enviar intentos de inicio de sesión.
Uso básico:
# Validar emails desde un archivo
Invoke-O365EmailValidator -File emails.txt -Output validados.txt
# El archivo emails.txt debe contener un email por línea:
# usuario1@contoso.com
# usuario2@contoso.com
# usuario3@fabrikam.com
# El output solo contendrá los emails que existen en O365Petición API manual:
POST /common/GetCredentialType HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/json
{
"username": "usuario@dominio.onmicrosoft.com",
"isOtherIdpSupported": true
}Ver LICENSE
