diff --git a/instalar.cmd b/instalar.cmd index 1b85199..926a3b9 100644 --- a/instalar.cmd +++ b/instalar.cmd @@ -1,24 +1,26 @@ -@echo off - -REM ============================================================ -REM Lanzador del instalador de Windows -REM ============================================================ - -powershell.exe ^ - -NoProfile ^ - -ExecutionPolicy Bypass ^ - -File "%~dp0setup.ps1" - -echo. -echo ============================================================ -echo El instalador ha terminado. -echo ============================================================ -echo. - -if errorlevel 1 ( - echo ERROR: el instalador ha terminado con errores. -) else ( - echo OK: el instalador ha terminado correctamente. -) - +@echo off + +REM ============================================================ +REM Lanzador del instalador de Windows +REM ============================================================ + +powershell.exe ^ + -NoProfile ^ + -ExecutionPolicy Bypass ^ + -File "%~dp0setup.ps1" + +echo. +echo ============================================================ +echo El instalador ha terminado. +echo ============================================================ +echo. +echo Codigo de salida: %errorlevel% +echo. + +if errorlevel 1 ( + echo ERROR: el instalador ha terminado con errores. +) else ( + echo OK: el instalador ha terminado correctamente. +) + pause \ No newline at end of file diff --git a/setup.ps1 b/setup.ps1 index 6caf3a1..09ce9d6 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -1,997 +1,1198 @@ -#Requires -Version 5.1 - -<# -.SYNOPSIS - Configuración inicial de Windows 10. - -.DESCRIPTION - Configura una cuenta técnica llamada "tecnica" y habilita - Escritorio Remoto. - - Características: - - Se autoeleva a Administrador. - - Crea "tecnica" si no existe. - - Solicita y confirma la contraseña al crearla. - - NO cambia la contraseña si "tecnica" ya existe. - - Añade "tecnica" al grupo Administradores. - - Activa Escritorio Remoto. - - Configura el Firewall para RDP. - - Añade "tecnica" al grupo Usuarios de escritorio remoto. - - Oculta "tecnica" de la lista de usuarios del login. - - Verifica el estado real al finalizar. - - Devuelve 0 si todo está correcto. - - Devuelve 1 si hay algún error. - -.NOTES - Compatible con Windows 10 / PowerShell 5.1. -#> - -# ============================================================ -# CONFIGURACIÓN -# ============================================================ - -$Username = "tecnica" - -# ============================================================ -# CONFIGURACIÓN DE ERRORES -# ============================================================ - -$ErrorActionPreference = "Stop" - -# ============================================================ -# FUNCIONES DE SALIDA -# ============================================================ - -function Write-Step { - param ( - [string]$Message - ) - - Write-Host "" - Write-Host "==================================================" -ForegroundColor DarkGray - Write-Host $Message -ForegroundColor Cyan - Write-Host "==================================================" -ForegroundColor DarkGray -} - -function Write-Ok { - param ( - [string]$Message - ) - - Write-Host "[OK] $Message" -ForegroundColor Green -} - -function Write-WarningMessage { - param ( - [string]$Message - ) - - Write-Host "[AVISO] $Message" -ForegroundColor Yellow -} - -function Write-ErrorMessage { - param ( - [string]$Message - ) - - Write-Host "[ERROR] $Message" -ForegroundColor Red -} - -# ============================================================ -# COMPROBAR ADMINISTRADOR -# ============================================================ - -function Test-IsAdministrator { - - $identity = [Security.Principal.WindowsIdentity]::GetCurrent() - - $principal = New-Object Security.Principal.WindowsPrincipal($identity) - - return $principal.IsInRole( - [Security.Principal.WindowsBuiltInRole]::Administrator - ) -} - -# ============================================================ -# AUTOELEVAR -# ============================================================ - -function Ensure-Administrator { - - if (Test-IsAdministrator) { - return - } - - Write-Host "" - Write-WarningMessage "Se necesitan permisos de Administrador." - Write-Host "Solicitando elevación mediante UAC..." -ForegroundColor Yellow - Write-Host "" - - if (-not $PSCommandPath) { - Write-ErrorMessage "No se puede determinar la ruta del script." - exit 1 - } - - try { - - $quotedScript = '"' + $PSCommandPath + '"' - - $process = Start-Process ` - -FilePath "powershell.exe" ` - -ArgumentList @( - "-NoProfile" - "-ExecutionPolicy" - "Bypass" - "-File" - $quotedScript - ) ` - -Verb RunAs ` - -Wait ` - -PassThru ` - -ErrorAction Stop - - exit $process.ExitCode - } - catch { - - Write-ErrorMessage "No se pudo obtener permisos de Administrador." - Write-ErrorMessage $_.Exception.Message - - exit 1 - } -} - -# ============================================================ -# OBTENER USUARIO LOCAL -# ============================================================ - -function Get-LocalUserSafe { - - param ( - [Parameter(Mandatory)] - [string]$Name - ) - - try { - return Get-LocalUser -Name $Name -ErrorAction Stop - } - catch { - return $null - } -} - -# ============================================================ -# OBTENER GRUPO LOCAL MEDIANTE SID -# ============================================================ - -function Get-LocalGroupBySid { - - param ( - [Parameter(Mandatory)] - [string]$Sid - ) - - $group = Get-LocalGroup -ErrorAction Stop | - Where-Object { - $_.SID.Value -eq $Sid - } | - Select-Object -First 1 - - if ($null -eq $group) { - throw "No se encontró el grupo con SID $Sid." - } - - return $group -} - -# ============================================================ -# COMPROBAR USUARIO EN GRUPO -# ============================================================ - -function Test-UserInGroup { - - param ( - [Parameter(Mandatory)] - [string]$Username, - - [Parameter(Mandatory)] - $Group - ) - - try { - - $members = Get-LocalGroupMember ` - -Group $Group.Name ` - -ErrorAction Stop - - foreach ($member in $members) { - - $memberName = $member.Name - - if ($memberName -match "\\") { - $memberName = $memberName.Split("\")[-1] - } - - if ($memberName -ieq $Username) { - return $true - } - } - - return $false - } - catch { - - return $false - } -} - -# ============================================================ -# AÑADIR USUARIO A GRUPO -# ============================================================ - -function Ensure-UserInGroup { - - param ( - [Parameter(Mandatory)] - [string]$Username, - - [Parameter(Mandatory)] - $Group - ) - - if (Test-UserInGroup -Username $Username -Group $Group) { - - Write-Ok "'$Username' ya pertenece a '$($Group.Name)'." - - return - } - - Write-Host "" - Write-Host "Añadiendo '$Username' a '$($Group.Name)'..." ` - -ForegroundColor Cyan - - Add-LocalGroupMember ` - -Group $Group.Name ` - -Member $Username ` - -ErrorAction Stop - - # Verificación inmediata. - - if (-not (Test-UserInGroup -Username $Username -Group $Group)) { - - throw "El usuario '$Username' no aparece en '$($Group.Name)' después de añadirlo." - } - - Write-Ok "'$Username' pertenece ahora a '$($Group.Name)'." -} - -# ============================================================ -# PEDIR Y CONFIRMAR CONTRASEÑA -# ============================================================ - -function Read-ConfirmedPassword { - - while ($true) { - - Write-Host "" - Write-Host "Introduce la contraseña para '$Username'." ` - -ForegroundColor Cyan - - $password1 = Read-Host "Contraseña" -AsSecureString - - $password2 = Read-Host "Repite la contraseña" -AsSecureString - - $ptr1 = [IntPtr]::Zero - $ptr2 = [IntPtr]::Zero - - $plain1 = $null - $plain2 = $null - - try { - - $ptr1 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR( - $password1 - ) - - $ptr2 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR( - $password2 - ) - - $plain1 = [Runtime.InteropServices.Marshal]::PtrToStringBSTR( - $ptr1 - ) - - $plain2 = [Runtime.InteropServices.Marshal]::PtrToStringBSTR( - $ptr2 - ) - - if ([string]::IsNullOrWhiteSpace($plain1)) { - - Write-ErrorMessage "La contraseña no puede estar vacía." - - continue - } - - if ($plain1 -cne $plain2) { - - Write-ErrorMessage "Las contraseñas no coinciden." - - continue - } - - return $password1 - } - finally { - - if ($ptr1 -ne [IntPtr]::Zero) { - - [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr1) - } - - if ($ptr2 -ne [IntPtr]::Zero) { - - [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr2) - } - - $plain1 = $null - $plain2 = $null - } - } -} - -# ============================================================ -# CREAR / COMPROBAR USUARIO TÉCNICO -# ============================================================ - -function Ensure-TechnicalUser { - - param ( - [Parameter(Mandatory)] - [string]$Name - ) - - $existingUser = Get-LocalUserSafe -Name $Name - - # -------------------------------------------------------- - # EL USUARIO YA EXISTE - # -------------------------------------------------------- - - if ($null -ne $existingUser) { - - Write-Ok "El usuario '$Name' ya existe." - - return $existingUser - } - - # -------------------------------------------------------- - # CREAR USUARIO - # -------------------------------------------------------- - - Write-Host "" - Write-Host "El usuario '$Name' no existe." ` - -ForegroundColor Yellow - - $password = Read-ConfirmedPassword - - Write-Host "" - Write-Host "Creando usuario '$Name'..." ` - -ForegroundColor Cyan - - try { - - New-LocalUser ` - -Name $Name ` - -Password $password ` - -Description "Cuenta técnica de administración" ` - -AccountNeverExpires ` - -PasswordNeverExpires ` - -ErrorAction Stop | Out-Null - - # Verificación real. - - $verifiedUser = Get-LocalUserSafe -Name $Name - - if ($null -eq $verifiedUser) { - - throw "New-LocalUser terminó pero el usuario '$Name' no existe." - } - - Write-Ok "Usuario '$Name' creado correctamente." - - return $verifiedUser - } - catch { - - throw "No se pudo crear el usuario '$Name': $($_.Exception.Message)" - } -} - -# ============================================================ -# ACTIVAR ESCRITORIO REMOTO -# ============================================================ - -function Enable-RemoteDesktop { - - Write-Host "" - Write-Host "Activando Escritorio remoto..." ` - -ForegroundColor Cyan - - # -------------------------------------------------------- - # REGISTRO - # -------------------------------------------------------- - - $rdpRegistryPath = - "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" - - Set-ItemProperty ` - -Path $rdpRegistryPath ` - -Name "fDenyTSConnections" ` - -Type DWord ` - -Value 0 ` - -ErrorAction Stop - - # Verificación. - - $rdpValue = ( - Get-ItemProperty ` - -Path $rdpRegistryPath ` - -Name "fDenyTSConnections" ` - -ErrorAction Stop - ).fDenyTSConnections - - if ($rdpValue -ne 0) { - - throw "Windows sigue teniendo RDP desactivado." - } - - Write-Ok "RDP está activado en el registro de Windows." - - # -------------------------------------------------------- - # SERVICIO - # -------------------------------------------------------- - - $service = Get-Service ` - -Name "TermService" ` - -ErrorAction Stop - - if ($service.StartType -ne "Manual") { - - Set-Service ` - -Name "TermService" ` - -StartupType Manual ` - -ErrorAction Stop - } - - $service = Get-Service ` - -Name "TermService" ` - -ErrorAction Stop - - if ($service.Status -ne "Running") { - - Start-Service ` - -Name "TermService" ` - -ErrorAction Stop - } - - # Verificación. - - $service = Get-Service ` - -Name "TermService" ` - -ErrorAction Stop - - if ($service.Status -ne "Running") { - - throw "El servicio TermService no está ejecutándose." - } - - Write-Ok "Servicio TermService funcionando." - - # -------------------------------------------------------- - # FIREWALL - # -------------------------------------------------------- - - $firewallRules = Get-NetFirewallRule ` - -ErrorAction Stop | - Where-Object { - $_.DisplayGroup -eq "Remote Desktop" -or - $_.DisplayGroup -eq "Escritorio remoto" - } - - if ($null -eq $firewallRules) { - - throw "No se encontraron las reglas de Firewall de Escritorio remoto." - } - - $firewallRules | - Enable-NetFirewallRule ` - -ErrorAction Stop - - # Verificación REAL de las reglas. - - $enabledRules = Get-NetFirewallRule ` - -ErrorAction Stop | - Where-Object { - ( - $_.DisplayGroup -eq "Remote Desktop" -or - $_.DisplayGroup -eq "Escritorio remoto" - ) -and - $_.Enabled -eq "True" - } - - if ($null -eq $enabledRules) { - - throw "No hay ninguna regla de Firewall de RDP habilitada." - } - - Write-Ok "Firewall de Escritorio remoto habilitado." -} - -# ============================================================ -# OCULTAR USUARIO DE LA PANTALLA DE LOGIN -# ============================================================ - -function Hide-UserFromLogin { - - param ( - [Parameter(Mandatory)] - [string]$Username - ) - - Write-Host "" - Write-Host "Ocultando '$Username' de la pantalla de login..." ` - -ForegroundColor Cyan - - $registryPath = - "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" - - # Crear la clave si no existe. - - if (-not (Test-Path $registryPath)) { - - New-Item ` - -Path $registryPath ` - -Force ` - -ErrorAction Stop | - Out-Null - } - - # 0 = ocultar usuario. - - New-ItemProperty ` - -Path $registryPath ` - -Name $Username ` - -PropertyType DWord ` - -Value 0 ` - -Force ` - -ErrorAction Stop | - Out-Null - - # Verificación. - - $value = ( - Get-ItemProperty ` - -Path $registryPath ` - -Name $Username ` - -ErrorAction Stop - ).$Username - - if ($value -ne 0) { - - throw "No se pudo ocultar '$Username' del login." - } - - Write-Ok "'$Username' está oculto de la lista de usuarios." -} - -# ============================================================ -# VERIFICACIÓN FINAL -# ============================================================ - -function Test-FinalConfiguration { - - Write-Step "VERIFICACIÓN FINAL DEL SISTEMA" - - $allOk = $true - - # -------------------------------------------------------- - # USUARIO - # -------------------------------------------------------- - - $user = Get-LocalUserSafe -Name $Username - - if ($null -ne $user) { - - Write-Ok "Usuario '$Username': EXISTE" - } - else { - - Write-ErrorMessage "Usuario '$Username': NO EXISTE" - - $allOk = $false - } - - # -------------------------------------------------------- - # ADMINISTRADORES - # -------------------------------------------------------- - - try { - - $administratorsGroup = - Get-LocalGroupBySid "S-1-5-32-544" - - if (Test-UserInGroup ` - -Username $Username ` - -Group $administratorsGroup) { - - Write-Ok "Administrador: SI" - } - else { - - Write-ErrorMessage "Administrador: NO" - - $allOk = $false - } - } - catch { - - Write-ErrorMessage "No se pudo comprobar el grupo Administradores." - - $allOk = $false - } - - # -------------------------------------------------------- - # RDP REGISTRO - # -------------------------------------------------------- - - try { - - $rdpRegistryPath = - "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" - - $rdpValue = ( - Get-ItemProperty ` - -Path $rdpRegistryPath ` - -Name "fDenyTSConnections" ` - -ErrorAction Stop - ).fDenyTSConnections - - if ($rdpValue -eq 0) { - - Write-Ok "Escritorio remoto de Windows: ACTIVADO" - } - else { - - Write-ErrorMessage "Escritorio remoto de Windows: DESACTIVADO" - - $allOk = $false - } - } - catch { - - Write-ErrorMessage "No se pudo comprobar la configuración RDP." - - $allOk = $false - } - - # -------------------------------------------------------- - # SERVICIO - # -------------------------------------------------------- - - try { - - $service = Get-Service ` - -Name "TermService" ` - -ErrorAction Stop - - if ($service.Status -eq "Running") { - - Write-Ok "TermService: EJECUTÁNDOSE" - } - else { - - Write-ErrorMessage "TermService: $($service.Status)" - - $allOk = $false - } - } - catch { - - Write-ErrorMessage "No se pudo comprobar TermService." - - $allOk = $false - } - - # -------------------------------------------------------- - # FIREWALL - # -------------------------------------------------------- - - try { - - $enabledRules = Get-NetFirewallRule ` - -ErrorAction Stop | - Where-Object { - ( - $_.DisplayGroup -eq "Remote Desktop" -or - $_.DisplayGroup -eq "Escritorio remoto" - ) -and - $_.Enabled -eq "True" - } - - if ($null -ne $enabledRules) { - - Write-Ok "Firewall RDP: HABILITADO" - } - else { - - Write-ErrorMessage "Firewall RDP: NO HABILITADO" - - $allOk = $false - } - } - catch { - - Write-ErrorMessage "No se pudo comprobar el Firewall de RDP." - - $allOk = $false - } - - # -------------------------------------------------------- - # GRUPO RDP - # -------------------------------------------------------- - - try { - - $rdpGroup = - Get-LocalGroupBySid "S-1-5-32-555" - - if (Test-UserInGroup ` - -Username $Username ` - -Group $rdpGroup) { - - Write-Ok "Usuario autorizado para RDP: SI" - } - else { - - Write-ErrorMessage "Usuario autorizado para RDP: NO" - - $allOk = $false - } - } - catch { - - Write-ErrorMessage "No se pudo comprobar el grupo RDP." - - $allOk = $false - } - - # -------------------------------------------------------- - # USUARIO OCULTO - # -------------------------------------------------------- - - try { - - $loginPath = - "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" - - $hiddenValue = ( - Get-ItemProperty ` - -Path $loginPath ` - -Name $Username ` - -ErrorAction Stop - ).$Username - - if ($hiddenValue -eq 0) { - - Write-Ok "Usuario oculto del login: SI" - } - else { - - Write-ErrorMessage "Usuario oculto del login: NO" - - $allOk = $false - } - } - catch { - - Write-ErrorMessage "No se pudo comprobar la ocultación del usuario." - - $allOk = $false - } - - # -------------------------------------------------------- - # RESULTADO - # -------------------------------------------------------- - - Write-Host "" - - if ($allOk) { - - Write-Host "==================================================" -ForegroundColor Green - Write-Host " TODO CORRECTO" -ForegroundColor Green - Write-Host "==================================================" -ForegroundColor Green - - return $true - } - else { - - Write-Host "==================================================" -ForegroundColor Red - Write-Host " HAY ERRORES" -ForegroundColor Red - Write-Host "==================================================" -ForegroundColor Red - - return $false - } -} - -# ============================================================ -# PROGRAMA PRINCIPAL -# ============================================================ - -try { - - Clear-Host - - Write-Host "" - Write-Host "==================================================" -ForegroundColor Cyan - Write-Host " WINDOWS 10 - CONFIGURACIÓN INICIAL" -ForegroundColor Cyan - Write-Host "==================================================" -ForegroundColor Cyan - Write-Host "" - - Write-Host "Usuario técnico: $Username" - Write-Host "" - - # -------------------------------------------------------- - # 0. ADMINISTRADOR - # -------------------------------------------------------- - - Ensure-Administrator - - Write-Ok "El script se está ejecutando como Administrador." - - # -------------------------------------------------------- - # 1. COMPROBAR GRUPOS - # -------------------------------------------------------- - - Write-Step "COMPROBANDO GRUPOS LOCALES" - - $administratorsGroup = - Get-LocalGroupBySid "S-1-5-32-544" - - $rdpGroup = - Get-LocalGroupBySid "S-1-5-32-555" - - Write-Ok "Grupo Administradores: $($administratorsGroup.Name)" - Write-Ok "Grupo Escritorio remoto: $($rdpGroup.Name)" - - # -------------------------------------------------------- - # 2. CREAR / COMPROBAR USUARIO - # -------------------------------------------------------- - - Write-Step "CREANDO / COMPROBANDO USUARIO" - - $user = Ensure-TechnicalUser -Name $Username - - # -------------------------------------------------------- - # 3. ASEGURAR USUARIO HABILITADO - # -------------------------------------------------------- - - if (-not $user.Enabled) { - - Write-Host "La cuenta está deshabilitada. Habilitándola..." ` - -ForegroundColor Yellow - - Enable-LocalUser ` - -Name $Username ` - -ErrorAction Stop - - $user = Get-LocalUserSafe -Name $Username - - if ($null -eq $user -or -not $user.Enabled) { - - throw "No se pudo habilitar el usuario '$Username'." - } - - Write-Ok "Usuario '$Username' habilitado." - } - else { - - Write-Ok "Usuario '$Username' está habilitado." - } - - # -------------------------------------------------------- - # 4. ADMINISTRADORES - # -------------------------------------------------------- - - Write-Step "CONFIGURANDO PRIVILEGIOS DE ADMINISTRADOR" - - Ensure-UserInGroup ` - -Username $Username ` - -Group $administratorsGroup - - # -------------------------------------------------------- - # 5. ESCRITORIO REMOTO - # -------------------------------------------------------- - - Write-Step "ACTIVANDO ESCRITORIO REMOTO" - - Enable-RemoteDesktop - - # -------------------------------------------------------- - # 6. USUARIO AUTORIZADO PARA RDP - # -------------------------------------------------------- - - Write-Step "AUTORIZANDO USUARIO PARA ESCRITORIO REMOTO" - - Ensure-UserInGroup ` - -Username $Username ` - -Group $rdpGroup - - # -------------------------------------------------------- - # 7. OCULTAR USUARIO - # -------------------------------------------------------- - - Write-Step "OCULTANDO USUARIO DE LA PANTALLA DE LOGIN" - - Hide-UserFromLogin ` - -Username $Username - - # -------------------------------------------------------- - # 8. VERIFICACIÓN FINAL - # -------------------------------------------------------- - - $result = Test-FinalConfiguration - - # -------------------------------------------------------- - # RESULTADO - # -------------------------------------------------------- - - Write-Host "" - - if ($result) { - - Write-Host "La configuración se ha completado correctamente." ` - -ForegroundColor Green - - Write-Host "" - Write-Host "El script se puede ejecutar de nuevo sin cambiar" ` - -ForegroundColor Green - Write-Host "la contraseña del usuario existente." ` - -ForegroundColor Green - - Write-Host "" - - exit 0 - } - else { - - Write-Host "La configuración NO se ha completado correctamente." ` - -ForegroundColor Red - - Write-Host "" - Write-Host "Revisa los errores mostrados anteriormente." ` - -ForegroundColor Yellow - - Write-Host "" - - exit 1 - } -} -catch { - - Write-Host "" - Write-Host "==================================================" -ForegroundColor Red - Write-Host " ERROR FATAL" -ForegroundColor Red - Write-Host "==================================================" -ForegroundColor Red - Write-Host "" - - Write-ErrorMessage $_.Exception.Message - - Write-Host "" - Write-Host "El script se ha detenido para evitar continuar" ` - -ForegroundColor Yellow - Write-Host "con una configuración incompleta." ` - -ForegroundColor Yellow - - Write-Host "" - - exit 1 +#Requires -Version 5.1 + +<# +.SYNOPSIS + Configuración inicial de Windows 10. + +.DESCRIPTION + Configura una cuenta técnica llamada "tecnica" y habilita + Escritorio Remoto. + + Características: + - Se autoeleva a Administrador. + - Crea "tecnica" si no existe. + - Solicita y confirma la contraseña al crearla. + - NO cambia la contraseña si "tecnica" ya existe. + - Añade "tecnica" al grupo Administradores. + - Activa Escritorio Remoto. + - Configura el Firewall para RDP. + - Añade "tecnica" al grupo Usuarios de escritorio remoto. + - Oculta "tecnica" de la lista de usuarios del login. + - Verifica el estado real al finalizar. + - Registra todos los pasos y errores en %TEMP%\crb-setup.log. + - Devuelve 0 si todo está correcto. + - Devuelve 1 si hay algún error. + +.NOTES + Compatible con Windows 10 / PowerShell 5.1. +#> + +# ============================================================ +# CONFIGURACIÓN +# ============================================================ + +$Username = "tecnica" + +# Ruta del log de diagnóstico. +$LogPath = Join-Path $env:TEMP "crb-setup.log" + +# ============================================================ +# CONFIGURACIÓN DE ERRORES +# ============================================================ + +$ErrorActionPreference = "Stop" + +# ============================================================ +# FUNCIONES DE SALIDA +# ============================================================ + +function Write-Step { + param ( + [string]$Message + ) + + Write-Host "" + Write-Host "==================================================" -ForegroundColor DarkGray + Write-Host $Message -ForegroundColor Cyan + Write-Host "==================================================" -ForegroundColor DarkGray + + Write-Log "[PASO] $Message" +} + +function Write-Ok { + param ( + [string]$Message + ) + + Write-Host "[OK] $Message" -ForegroundColor Green + + Write-Log "[OK] $Message" +} + +function Write-WarningMessage { + param ( + [string]$Message + ) + + Write-Host "[AVISO] $Message" -ForegroundColor Yellow + + Write-Log "[AVISO] $Message" +} + +function Write-ErrorMessage { + param ( + [string]$Message + ) + + Write-Host "[ERROR] $Message" -ForegroundColor Red + + Write-Log "[ERROR] $Message" +} + +# ============================================================ +# ESCRIBIR EN LOG +# ============================================================ + +function Write-Log { + + param ( + [string]$Message + ) + + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + + try { + + Add-Content ` + -LiteralPath $LogPath ` + -Value "$timestamp $Message" ` + -Encoding UTF8 ` + -ErrorAction SilentlyContinue + } + catch { + # El log nunca debe interrumpir la ejecución del script. + } +} + +# ============================================================ +# OBTENER MENSAJE COMPLETO DE UNA EXCEPCIÓN +# ============================================================ + +function Get-ExceptionMessage { + + param ( + [Parameter(Mandatory)] + [System.Exception]$Exception + ) + + $parts = New-Object System.Collections.Generic.List[string] + + $current = $Exception + + while ($null -ne $current) { + + $parts.Add($current.Message) + + $current = $current.InnerException + } + + return ($parts -join " << ") +} + +# ============================================================ +# COMPROBAR ADMINISTRADOR +# ============================================================ + +function Test-IsAdministrator { + + $identity = [Security.Principal.WindowsIdentity]::GetCurrent() + + $principal = New-Object Security.Principal.WindowsPrincipal($identity) + + return $principal.IsInRole( + [Security.Principal.WindowsBuiltInRole]::Administrator + ) +} + +# ============================================================ +# AUTOELEVAR +# ============================================================ + +function Ensure-Administrator { + + if (Test-IsAdministrator) { + return + } + + Write-Host "" + Write-WarningMessage "Se necesitan permisos de Administrador." + Write-Host "Solicitando elevación mediante UAC..." -ForegroundColor Yellow + Write-Host "" + + if (-not $PSCommandPath) { + Write-ErrorMessage "No se puede determinar la ruta del script." + exit 1 + } + + # -------------------------------------------------------- + # ELEVACIÓN DESDE RUTA DE RED (UNC) + # -------------------------------------------------------- + # El proceso elevado puede perder el acceso a la carpeta de + # red (\\servidor\...). Para evitarlo, se copia el script + # a %TEMP% y se eleva ejecutando la copia local. + + $scriptPath = $PSCommandPath + $tempCopy = $null + + if ($scriptPath -like "\\*") { + + Write-WarningMessage "El script está en una ruta de red (UNC)." + Write-Host "Copiando a la carpeta temporal para poder elevarse..." ` + -ForegroundColor Yellow + + $tempCopy = Join-Path $env:TEMP ( + "crb-setup-" + [guid]::NewGuid().ToString("N") + ".ps1" + ) + + try { + + Copy-Item ` + -LiteralPath $PSCommandPath ` + -Destination $tempCopy ` + -Force ` + -ErrorAction Stop + } + catch { + + Write-ErrorMessage "No se pudo copiar el script a la carpeta temporal." + Write-ErrorMessage $_.Exception.Message + + exit 1 + } + + $scriptPath = $tempCopy + } + + $quotedScript = '"' + $scriptPath + '"' + + try { + + $process = Start-Process ` + -FilePath "powershell.exe" ` + -ArgumentList @( + "-NoProfile" + "-ExecutionPolicy" + "Bypass" + "-File" + $quotedScript + ) ` + -Verb RunAs ` + -Wait ` + -PassThru ` + -ErrorAction Stop + } + catch { + + if ($tempCopy) { + Remove-Item ` + -LiteralPath $tempCopy ` + -Force ` + -ErrorAction SilentlyContinue + } + + Write-ErrorMessage "No se pudo obtener permisos de Administrador." + Write-ErrorMessage $_.Exception.Message + + exit 1 + } + + if ($tempCopy) { + Remove-Item ` + -LiteralPath $tempCopy ` + -Force ` + -ErrorAction SilentlyContinue + } + + exit $process.ExitCode +} + +# ============================================================ +# OBTENER USUARIO LOCAL +# ============================================================ + +function Get-LocalUserSafe { + + param ( + [Parameter(Mandatory)] + [string]$Name + ) + + try { + return Get-LocalUser -Name $Name -ErrorAction Stop + } + catch { + return $null + } +} + +# ============================================================ +# OBTENER GRUPO LOCAL MEDIANTE SID +# ============================================================ + +function Get-LocalGroupBySid { + + param ( + [Parameter(Mandatory)] + [string]$Sid + ) + + $group = Get-LocalGroup -ErrorAction Stop | + Where-Object { + $_.SID.Value -eq $Sid + } | + Select-Object -First 1 + + if ($null -eq $group) { + throw "No se encontró el grupo con SID $Sid." + } + + return $group +} + +# ============================================================ +# COMPROBAR USUARIO EN GRUPO +# ============================================================ + +function Test-UserInGroup { + + param ( + [Parameter(Mandatory)] + [string]$Username, + + [Parameter(Mandatory)] + $Group + ) + + try { + + $members = Get-LocalGroupMember ` + -Group $Group.Name ` + -ErrorAction Stop + + foreach ($member in $members) { + + $memberName = $member.Name + + if ($memberName -match "\\") { + $memberName = $memberName.Split("\")[-1] + } + + if ($memberName -ieq $Username) { + return $true + } + } + + return $false + } + catch { + + return $false + } +} + +# ============================================================ +# AÑADIR USUARIO A GRUPO +# ============================================================ + +function Ensure-UserInGroup { + + param ( + [Parameter(Mandatory)] + [string]$Username, + + [Parameter(Mandatory)] + $Group + ) + + if (Test-UserInGroup -Username $Username -Group $Group) { + + Write-Ok "'$Username' ya pertenece a '$($Group.Name)'." + + return + } + + Write-Host "" + Write-Host "Añadiendo '$Username' a '$($Group.Name)'..." ` + -ForegroundColor Cyan + + Add-LocalGroupMember ` + -Group $Group.Name ` + -Member $Username ` + -ErrorAction Stop + + # Verificación inmediata. + + if (-not (Test-UserInGroup -Username $Username -Group $Group)) { + + throw "El usuario '$Username' no aparece en '$($Group.Name)' después de añadirlo." + } + + Write-Ok "'$Username' pertenece ahora a '$($Group.Name)'." +} + +# ============================================================ +# PEDIR Y CONFIRMAR CONTRASEÑA +# ============================================================ + +function Read-ConfirmedPassword { + + while ($true) { + + Write-Host "" + Write-Host "Introduce la contraseña para '$Username'." ` + -ForegroundColor Cyan + + $password1 = Read-Host "Contraseña" -AsSecureString + + $password2 = Read-Host "Repite la contraseña" -AsSecureString + + $ptr1 = [IntPtr]::Zero + $ptr2 = [IntPtr]::Zero + + $plain1 = $null + $plain2 = $null + + try { + + $ptr1 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR( + $password1 + ) + + $ptr2 = [Runtime.InteropServices.Marshal]::SecureStringToBSTR( + $password2 + ) + + $plain1 = [Runtime.InteropServices.Marshal]::PtrToStringBSTR( + $ptr1 + ) + + $plain2 = [Runtime.InteropServices.Marshal]::PtrToStringBSTR( + $ptr2 + ) + + if ([string]::IsNullOrWhiteSpace($plain1)) { + + Write-ErrorMessage "La contraseña no puede estar vacía." + + continue + } + + if ($plain1 -cne $plain2) { + + Write-ErrorMessage "Las contraseñas no coinciden." + + continue + } + + return $password1 + } + finally { + + if ($ptr1 -ne [IntPtr]::Zero) { + + [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr1) + } + + if ($ptr2 -ne [IntPtr]::Zero) { + + [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr2) + } + + $plain1 = $null + $plain2 = $null + } + } +} + +# ============================================================ +# CREAR / COMPROBAR USUARIO TÉCNICO +# ============================================================ + +function Ensure-TechnicalUser { + + param ( + [Parameter(Mandatory)] + [string]$Name + ) + + $existingUser = Get-LocalUserSafe -Name $Name + + # -------------------------------------------------------- + # EL USUARIO YA EXISTE + # -------------------------------------------------------- + + if ($null -ne $existingUser) { + + Write-Ok "El usuario '$Name' ya existe." + + return $existingUser + } + + # -------------------------------------------------------- + # CREAR USUARIO + # -------------------------------------------------------- + + Write-Host "" + Write-Host "El usuario '$Name' no existe." ` + -ForegroundColor Yellow + + $password = Read-ConfirmedPassword + + Write-Host "" + Write-Host "Creando usuario '$Name'..." ` + -ForegroundColor Cyan + + try { + + New-LocalUser ` + -Name $Name ` + -Password $password ` + -Description "Cuenta técnica de administración" ` + -AccountNeverExpires ` + -PasswordNeverExpires ` + -ErrorAction Stop | Out-Null + + # Verificación real. + + $verifiedUser = Get-LocalUserSafe -Name $Name + + if ($null -eq $verifiedUser) { + + throw "New-LocalUser terminó pero el usuario '$Name' no existe." + } + + Write-Ok "Usuario '$Name' creado correctamente." + + return $verifiedUser + } + catch { + + throw "No se pudo crear el usuario '$Name': $($_.Exception.Message)" + } +} + +# ============================================================ +# OBTENER REGLAS DE FIREWALL DE ESCRITORIO REMOTO +# ============================================================ + +function Get-RdpFirewallRules { + + $rdpGroupNames = @("Remote Desktop", "Escritorio remoto") + + $rules = @( + Get-NetFirewallRule ` + -ErrorAction Stop | + Where-Object { + $rdpGroupNames -contains $_.DisplayGroup + } + ) + + return $rules +} + +# ============================================================ +# ACTIVAR ESCRITORIO REMOTO +# ============================================================ + +function Enable-RemoteDesktop { + + Write-Host "" + Write-Host "Activando Escritorio remoto..." ` + -ForegroundColor Cyan + + # -------------------------------------------------------- + # REGISTRO + # -------------------------------------------------------- + + $rdpRegistryPath = + "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" + + Set-ItemProperty ` + -Path $rdpRegistryPath ` + -Name "fDenyTSConnections" ` + -Type DWord ` + -Value 0 ` + -ErrorAction Stop + + # Verificación. + + $rdpValue = ( + Get-ItemProperty ` + -Path $rdpRegistryPath ` + -Name "fDenyTSConnections" ` + -ErrorAction Stop + ).fDenyTSConnections + + if ($rdpValue -ne 0) { + + throw "Windows sigue teniendo RDP desactivado." + } + + Write-Ok "RDP está activado en el registro de Windows." + + # -------------------------------------------------------- + # SERVICIO + # -------------------------------------------------------- + + $service = Get-Service ` + -Name "TermService" ` + -ErrorAction Stop + + if ($service.StartType -ne "Manual") { + + Set-Service ` + -Name "TermService" ` + -StartupType Manual ` + -ErrorAction Stop + } + + $service = Get-Service ` + -Name "TermService" ` + -ErrorAction Stop + + if ($service.Status -ne "Running") { + + Start-Service ` + -Name "TermService" ` + -ErrorAction Stop + } + + # Verificación. + + $service = Get-Service ` + -Name "TermService" ` + -ErrorAction Stop + + if ($service.Status -ne "Running") { + + throw "El servicio TermService no está ejecutándose." + } + + Write-Ok "Servicio TermService funcionando." + + # -------------------------------------------------------- + # SERVICIO DE FIREWALL + # -------------------------------------------------------- + + $firewallService = Get-Service ` + -Name "mpssvc" ` + -ErrorAction Stop + + if ($firewallService.Status -ne "Running") { + + Write-Host "El servicio de Firewall no está activo. Iniciándolo..." ` + -ForegroundColor Yellow + + if ($firewallService.StartType -eq "Disabled") { + + Set-Service ` + -Name "mpssvc" ` + -StartupType Automatic ` + -ErrorAction Stop + } + + Start-Service ` + -Name "mpssvc" ` + -ErrorAction Stop + + $firewallService = Get-Service ` + -Name "mpssvc" ` + -ErrorAction Stop + + if ($firewallService.Status -ne "Running") { + + throw "No se pudo iniciar el servicio de Firewall (mpssvc)." + } + } + + Write-Ok "Servicio de Firewall de Windows activo." + + # -------------------------------------------------------- + # FIREWALL + # -------------------------------------------------------- + + $rules = Get-RdpFirewallRules + + if ($rules.Count -eq 0) { + + Write-Host "No se hallaron reglas RDP por grupo. Probando con netsh..." ` + -ForegroundColor Yellow + + & netsh.exe advfirewall firewall set rule group="Remote Desktop" new enable=yes | Out-Null + + & netsh.exe advfirewall firewall set rule group="Escritorio remoto" new enable=yes | Out-Null + + $rules = Get-RdpFirewallRules + } + + if ($rules.Count -eq 0) { + + throw "No se encontraron reglas de Firewall de Escritorio remoto (grupo 'Remote Desktop'/'Escritorio remoto')." + } + + $rules | + Enable-NetFirewallRule ` + -ErrorAction Stop + + # Verificación REAL de las reglas. + + $enabledRules = @( + Get-RdpFirewallRules | + Where-Object { + $_.Enabled -eq "True" + } + ) + + if ($enabledRules.Count -eq 0) { + + throw "No hay ninguna regla de Firewall de RDP habilitada." + } + + Write-Ok "Firewall de Escritorio remoto habilitado." +} + +# ============================================================ +# OCULTAR USUARIO DE LA PANTALLA DE LOGIN +# ============================================================ + +function Hide-UserFromLogin { + + param ( + [Parameter(Mandatory)] + [string]$Username + ) + + Write-Host "" + Write-Host "Ocultando '$Username' de la pantalla de login..." ` + -ForegroundColor Cyan + + $registryPath = + "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" + + # Crear la clave si no existe. + + if (-not (Test-Path $registryPath)) { + + New-Item ` + -Path $registryPath ` + -Force ` + -ErrorAction Stop | + Out-Null + } + + # 0 = ocultar usuario. + + New-ItemProperty ` + -Path $registryPath ` + -Name $Username ` + -PropertyType DWord ` + -Value 0 ` + -Force ` + -ErrorAction Stop | + Out-Null + + # Verificación. + + $value = ( + Get-ItemProperty ` + -Path $registryPath ` + -Name $Username ` + -ErrorAction Stop + ).$Username + + if ($value -ne 0) { + + throw "No se pudo ocultar '$Username' del login." + } + + Write-Ok "'$Username' está oculto de la lista de usuarios." +} + +# ============================================================ +# VERIFICACIÓN FINAL +# ============================================================ + +function Test-FinalConfiguration { + + Write-Step "VERIFICACIÓN FINAL DEL SISTEMA" + + $allOk = $true + + # -------------------------------------------------------- + # USUARIO + # -------------------------------------------------------- + + $user = Get-LocalUserSafe -Name $Username + + if ($null -ne $user) { + + Write-Ok "Usuario '$Username': EXISTE" + } + else { + + Write-ErrorMessage "Usuario '$Username': NO EXISTE" + + $allOk = $false + } + + # -------------------------------------------------------- + # ADMINISTRADORES + # -------------------------------------------------------- + + try { + + $administratorsGroup = + Get-LocalGroupBySid "S-1-5-32-544" + + if (Test-UserInGroup ` + -Username $Username ` + -Group $administratorsGroup) { + + Write-Ok "Administrador: SI" + } + else { + + Write-ErrorMessage "Administrador: NO" + + $allOk = $false + } + } + catch { + + Write-ErrorMessage "No se pudo comprobar el grupo Administradores." + + $allOk = $false + } + + # -------------------------------------------------------- + # RDP REGISTRO + # -------------------------------------------------------- + + try { + + $rdpRegistryPath = + "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" + + $rdpValue = ( + Get-ItemProperty ` + -Path $rdpRegistryPath ` + -Name "fDenyTSConnections" ` + -ErrorAction Stop + ).fDenyTSConnections + + if ($rdpValue -eq 0) { + + Write-Ok "Escritorio remoto de Windows: ACTIVADO" + } + else { + + Write-ErrorMessage "Escritorio remoto de Windows: DESACTIVADO" + + $allOk = $false + } + } + catch { + + Write-ErrorMessage "No se pudo comprobar la configuración RDP." + + $allOk = $false + } + + # -------------------------------------------------------- + # SERVICIO + # -------------------------------------------------------- + + try { + + $service = Get-Service ` + -Name "TermService" ` + -ErrorAction Stop + + if ($service.Status -eq "Running") { + + Write-Ok "TermService: EJECUTÁNDOSE" + } + else { + + Write-ErrorMessage "TermService: $($service.Status)" + + $allOk = $false + } + } + catch { + + Write-ErrorMessage "No se pudo comprobar TermService." + + $allOk = $false + } + + # -------------------------------------------------------- + # FIREWALL + # -------------------------------------------------------- + + try { + + $enabledRules = @( + Get-RdpFirewallRules | + Where-Object { + $_.Enabled -eq "True" + } + ) + + if ($enabledRules.Count -gt 0) { + + Write-Ok "Firewall RDP: HABILITADO" + } + else { + + $allRdpRules = @(Get-RdpFirewallRules) + + if ($allRdpRules.Count -eq 0) { + + Write-ErrorMessage "Firewall RDP: NO HAY REGLAS RDP EN EL SISTEMA" + } + else { + + Write-ErrorMessage "Firewall RDP: LAS REGLAS EXISTEN PERO ESTÁN DESHABILITADAS" + } + + $allOk = $false + } + } + catch { + + Write-ErrorMessage "No se pudo comprobar el Firewall de RDP: $($_.Exception.Message)" + + $allOk = $false + } + + # -------------------------------------------------------- + # GRUPO RDP + # -------------------------------------------------------- + + try { + + $rdpGroup = + Get-LocalGroupBySid "S-1-5-32-555" + + if (Test-UserInGroup ` + -Username $Username ` + -Group $rdpGroup) { + + Write-Ok "Usuario autorizado para RDP: SI" + } + else { + + Write-ErrorMessage "Usuario autorizado para RDP: NO" + + $allOk = $false + } + } + catch { + + Write-ErrorMessage "No se pudo comprobar el grupo RDP." + + $allOk = $false + } + + # -------------------------------------------------------- + # USUARIO OCULTO + # -------------------------------------------------------- + + try { + + $loginPath = + "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" + + $hiddenValue = ( + Get-ItemProperty ` + -Path $loginPath ` + -Name $Username ` + -ErrorAction Stop + ).$Username + + if ($hiddenValue -eq 0) { + + Write-Ok "Usuario oculto del login: SI" + } + else { + + Write-ErrorMessage "Usuario oculto del login: NO" + + $allOk = $false + } + } + catch { + + Write-ErrorMessage "No se pudo comprobar la ocultación del usuario." + + $allOk = $false + } + + # -------------------------------------------------------- + # RESULTADO + # -------------------------------------------------------- + + Write-Host "" + + if ($allOk) { + + Write-Host "==================================================" -ForegroundColor Green + Write-Host " TODO CORRECTO" -ForegroundColor Green + Write-Host "==================================================" -ForegroundColor Green + + return $true + } + else { + + Write-Host "==================================================" -ForegroundColor Red + Write-Host " HAY ERRORES" -ForegroundColor Red + Write-Host "==================================================" -ForegroundColor Red + + return $false + } +} + +# ============================================================ +# PROGRAMA PRINCIPAL +# ============================================================ + +try { + + Clear-Host + + Write-Log "========== INICIO DE EJECUCIÓN ==========" + Write-Log "Windows: $([Environment]::OSVersion.VersionString)" + Write-Log "Usuario: $([Environment]::UserDomainName)\$([Environment]::UserName)" + Write-Log "Elevado (Administrador): $(Test-IsAdministrator)" + + Write-Host "" + Write-Host "==================================================" -ForegroundColor Cyan + Write-Host " WINDOWS 10 - CONFIGURACIÓN INICIAL" -ForegroundColor Cyan + Write-Host "==================================================" -ForegroundColor Cyan + Write-Host "" + + Write-Host "Usuario técnico: $Username" + Write-Host "" + + # -------------------------------------------------------- + # 0. ADMINISTRADOR + # -------------------------------------------------------- + + Ensure-Administrator + + Write-Ok "El script se está ejecutando como Administrador." + + # -------------------------------------------------------- + # 1. COMPROBAR GRUPOS + # -------------------------------------------------------- + + Write-Step "COMPROBANDO GRUPOS LOCALES" + + $administratorsGroup = + Get-LocalGroupBySid "S-1-5-32-544" + + $rdpGroup = + Get-LocalGroupBySid "S-1-5-32-555" + + Write-Ok "Grupo Administradores: $($administratorsGroup.Name)" + Write-Ok "Grupo Escritorio remoto: $($rdpGroup.Name)" + + # -------------------------------------------------------- + # 2. CREAR / COMPROBAR USUARIO + # -------------------------------------------------------- + + Write-Step "CREANDO / COMPROBANDO USUARIO" + + $user = Ensure-TechnicalUser -Name $Username + + # -------------------------------------------------------- + # 3. ASEGURAR USUARIO HABILITADO + # -------------------------------------------------------- + + if (-not $user.Enabled) { + + Write-Host "La cuenta está deshabilitada. Habilitándola..." ` + -ForegroundColor Yellow + + Enable-LocalUser ` + -Name $Username ` + -ErrorAction Stop + + $user = Get-LocalUserSafe -Name $Username + + if ($null -eq $user -or -not $user.Enabled) { + + throw "No se pudo habilitar el usuario '$Username'." + } + + Write-Ok "Usuario '$Username' habilitado." + } + else { + + Write-Ok "Usuario '$Username' está habilitado." + } + + # -------------------------------------------------------- + # 4. ADMINISTRADORES + # -------------------------------------------------------- + + Write-Step "CONFIGURANDO PRIVILEGIOS DE ADMINISTRADOR" + + Ensure-UserInGroup ` + -Username $Username ` + -Group $administratorsGroup + + # -------------------------------------------------------- + # 5. ESCRITORIO REMOTO + # -------------------------------------------------------- + + Write-Step "ACTIVANDO ESCRITORIO REMOTO" + + Enable-RemoteDesktop + + # -------------------------------------------------------- + # 6. USUARIO AUTORIZADO PARA RDP + # -------------------------------------------------------- + + Write-Step "AUTORIZANDO USUARIO PARA ESCRITORIO REMOTO" + + Ensure-UserInGroup ` + -Username $Username ` + -Group $rdpGroup + + # -------------------------------------------------------- + # 7. OCULTAR USUARIO + # -------------------------------------------------------- + + Write-Step "OCULTANDO USUARIO DE LA PANTALLA DE LOGIN" + + Hide-UserFromLogin ` + -Username $Username + + # -------------------------------------------------------- + # 8. VERIFICACIÓN FINAL + # -------------------------------------------------------- + + $result = Test-FinalConfiguration + + # -------------------------------------------------------- + # RESULTADO + # -------------------------------------------------------- + + Write-Host "" + + if ($result) { + + Write-Host "La configuración se ha completado correctamente." ` + -ForegroundColor Green + + Write-Host "" + Write-Host "El script se puede ejecutar de nuevo sin cambiar" ` + -ForegroundColor Green + Write-Host "la contraseña del usuario existente." ` + -ForegroundColor Green + + Write-Host "" + + exit 0 + } + else { + + Write-Host "La configuración NO se ha completado correctamente." ` + -ForegroundColor Red + + Write-Host "" + Write-Host "Revisa los errores mostrados anteriormente." ` + -ForegroundColor Yellow + + Write-Host "" + + exit 1 + } +} +catch { + + $fullMessage = Get-ExceptionMessage -Exception $_.Exception + + Write-Host "" + Write-Host "==================================================" -ForegroundColor Red + Write-Host " ERROR FATAL" -ForegroundColor Red + Write-Host "==================================================" -ForegroundColor Red + Write-Host "" + + Write-ErrorMessage $fullMessage + + Write-Host "" + Write-Host "Trace del script:" -ForegroundColor Yellow + Write-Host $_.ScriptStackTrace + + Write-Log "==========================================================================" + Write-Log "ERROR FATAL: $fullMessage" + Write-Log ("TRACE: " + ($_.ScriptStackTrace -replace "(`r)?`n", " | ")) + Write-Log "==========================================================================" + + Write-Host "" + Write-Host "El script se ha detenido para evitar continuar" ` + -ForegroundColor Yellow + Write-Host "con una configuración incompleta." ` + -ForegroundColor Yellow + + Write-Host "" + Write-Host "Detalles guardados en el log: $LogPath" ` + -ForegroundColor Cyan + + Write-Host "" + + exit 1 } \ No newline at end of file