Public/Get-TechCareSecurity.ps1

function Get-TechCareSecurity {
    <#
    .SYNOPSIS
        Revision de seguridad del equipo, con correcciones seguras opcionales.
    .DESCRIPTION
        Revisa Microsoft Defender, la antiguedad de las firmas, el firewall, UAC,
        BitLocker, SMBv1, cuentas locales sin contrasena obligatoria, RDP y los
        parches pendientes.

        Sin -Apply solo informa. Con -Apply aplica unicamente tres correcciones
        que no pueden dejar al usuario fuera de su equipo ni romper una sesion en
        curso: activar el firewall, actualizar las firmas de Defender y desactivar
        SMBv1. Todo lo demas (UAC, BitLocker, RDP, cuentas) queda como
        recomendacion, porque aplicarlo a ciegas rompe equipos: UAC necesita
        reinicio, BitLocker necesita una clave de recuperacion custodiada y cerrar
        RDP puede dejar sin acceso al propio tecnico.

        TechCare nunca desactiva Defender ni Windows Update.
    .PARAMETER Apply
        Aplica las tres correcciones seguras. Requiere administrador.
    .EXAMPLE
        Get-TechCareSecurity

        Solo revisa y devuelve hallazgos y recomendaciones.
    .EXAMPLE
        Get-TechCareSecurity -Apply -WhatIf

        Muestra que correcciones seguras se aplicarian.
    .EXAMPLE
        Get-TechCareSecurity -Apply -Confirm:$false

        Aplica las correcciones seguras sin preguntar.
    .OUTPUTS
        PSCustomObject
    .LINK
        https://github.com/earbona23/TechCare
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    [OutputType([pscustomobject])]
    param(
        [switch]$Apply
    )

    if (-not (Test-TechCarePlatform)) { throw $script:PlatformMessage }

    $findings = New-Object -TypeName System.Collections.ArrayList
    $actions  = New-Object -TypeName System.Collections.ArrayList
    $recs     = New-Object -TypeName System.Collections.ArrayList
    $status   = 'Green'
    $mod      = 'Security'
    $admin    = Test-TechCareAdmin
    $canFix   = ($Apply -and $admin)

    if ($Apply -and -not $admin) {
        [void]$findings.Add('-Apply solicitado sin privilegios de administrador: solo se revisa.')
        [void]$recs.Add('Relanzar como administrador para aplicar las correcciones seguras.')
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando Defender' -PercentComplete 10
    try {
        $mp = Get-MpComputerStatus -ErrorAction Stop
        [void]$findings.Add(('Defender - tiempo real: {0} / antivirus: {1} / antiguedad de firmas: {2} dias' -f
            $mp.RealTimeProtectionEnabled, $mp.AntivirusEnabled, $mp.AntivirusSignatureAge))
        if (-not $mp.RealTimeProtectionEnabled) {
            $status = 'Red'
            [void]$recs.Add('Proteccion en tiempo real de Defender apagada: averiguar si la apago un antivirus de terceros o una politica antes de reactivarla.')
        }
        if ($mp.AntivirusSignatureAge -gt 3) {
            if ($status -ne 'Red') { $status = 'Yellow' }
            [void]$recs.Add('Firmas de Defender con mas de 3 dias.')
            if ($canFix -and $PSCmdlet.ShouldProcess('Microsoft Defender', 'Actualizar firmas')) {
                try {
                    Update-MpSignature -ErrorAction Stop
                    [void]$actions.Add('Firmas de Defender actualizadas.')
                } catch {
                    [void]$findings.Add('No se pudieron actualizar las firmas: ' + $_.Exception.Message)
                }
            }
        }
    } catch {
        [void]$findings.Add('Defender no disponible o sin acceso (probable antivirus de terceros): ' + $_.Exception.Message)
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando firewall' -PercentComplete 25
    try {
        $fwProfiles = Get-NetFirewallProfile -ErrorAction Stop
        foreach ($p in $fwProfiles) {
            [void]$findings.Add(('Firewall perfil {0}: {1}' -f $p.Name, $p.Enabled))
            if ($p.Enabled) { continue }
            $status = 'Red'
            [void]$recs.Add(('Firewall apagado en el perfil {0}.' -f $p.Name))
            if ($canFix -and $PSCmdlet.ShouldProcess(('Firewall, perfil ' + $p.Name), 'Activar')) {
                try {
                    Set-NetFirewallProfile -Profile $p.Name -Enabled True -ErrorAction Stop
                    [void]$actions.Add(('Firewall activado en el perfil {0}.' -f $p.Name))
                } catch {
                    [void]$findings.Add(('No se pudo activar el firewall {0}: {1}' -f $p.Name, $_.Exception.Message))
                }
            }
        }
    } catch {
        [void]$findings.Add('No se pudo consultar el firewall: ' + $_.Exception.Message)
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando UAC' -PercentComplete 40
    try {
        $uacKey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
        $uac = Get-ItemProperty -LiteralPath $uacKey -ErrorAction Stop
        $enableLua = $uac.PSObject.Properties['EnableLUA']
        $consent   = $uac.PSObject.Properties['ConsentPromptBehaviorAdmin']
        if ($null -ne $enableLua) {
            [void]$findings.Add(('UAC EnableLUA: {0}' -f $enableLua.Value))
            if ($enableLua.Value -ne 1) {
                $status = 'Red'
                [void]$recs.Add('UAC desactivado: reactivarlo requiere reinicio, por eso TechCare no lo toca.')
            } elseif ($null -ne $consent -and $consent.Value -eq 0) {
                if ($status -ne 'Red') { $status = 'Yellow' }
                [void]$recs.Add('UAC eleva sin preguntar (ConsentPromptBehaviorAdmin = 0): valorar subir el nivel.')
            }
        }
    } catch {
        [void]$findings.Add('No se pudo leer la configuracion de UAC: ' + $_.Exception.Message)
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando BitLocker' -PercentComplete 55
    try {
        $systemVolume = $env:SystemDrive + '\'
        $bl = @(Get-BitLockerVolume -ErrorAction Stop | Where-Object { $_.MountPoint -eq $systemVolume })
        if ($bl.Count -gt 0) {
            [void]$findings.Add(('BitLocker en {0}: {1}' -f $bl[0].MountPoint, $bl[0].ProtectionStatus))
            if ($bl[0].ProtectionStatus -ne 'On') {
                if ($status -ne 'Red') { $status = 'Yellow' }
                [void]$recs.Add('BitLocker apagado en el volumen del sistema. Activarlo exige custodiar la clave de recuperacion: decision del responsable, no del script.')
            }
        } else {
            [void]$findings.Add('BitLocker: el volumen del sistema no aparece como volumen cifrable.')
        }
    } catch {
        [void]$findings.Add('BitLocker no consultable (sin administrador o edicion no compatible): ' + $_.Exception.Message)
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando SMBv1' -PercentComplete 65
    try {
        $smb = Get-SmbServerConfiguration -ErrorAction Stop
        [void]$findings.Add(('SMBv1 habilitado: {0}' -f $smb.EnableSMB1Protocol))
        if ($smb.EnableSMB1Protocol) {
            if ($status -ne 'Red') { $status = 'Yellow' }
            [void]$recs.Add('SMBv1 habilitado. Antes de quitarlo, confirmar que no hay NAS ni impresoras antiguas que solo hablen SMBv1.')
            if ($canFix -and $PSCmdlet.ShouldProcess('SMBv1 (servidor)', 'Desactivar el protocolo')) {
                try {
                    Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force -ErrorAction Stop
                    [void]$actions.Add('SMBv1 desactivado en el servidor SMB.')
                } catch {
                    [void]$findings.Add('No se pudo desactivar SMBv1: ' + $_.Exception.Message)
                }
            }
        }
    } catch {
        [void]$findings.Add('No se pudo consultar la configuracion SMB: ' + $_.Exception.Message)
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando cuentas locales' -PercentComplete 78
    try {
        $weak = @(Get-LocalUser -ErrorAction Stop | Where-Object { $_.Enabled -and -not $_.PasswordRequired })
        if ($weak.Count -gt 0) {
            if ($status -ne 'Red') { $status = 'Yellow' }
            foreach ($u in $weak) { [void]$findings.Add('Cuenta local activa sin contrasena obligatoria: ' + $u.Name) }
            [void]$recs.Add('Poner contrasena a las cuentas locales listadas, o deshabilitarlas si no se usan.')
        } else {
            [void]$findings.Add('Cuentas locales activas sin contrasena obligatoria: 0')
        }
    } catch {
        [void]$findings.Add('No se pudo enumerar cuentas locales: ' + $_.Exception.Message)
    }

    Write-Progress -Activity 'TechCare' -Status 'Revisando RDP' -PercentComplete 88
    try {
        $tsKey = 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server'
        $rdp = Get-ItemProperty -LiteralPath $tsKey -Name 'fDenyTSConnections' -ErrorAction Stop
        $rdpOn = ($rdp.fDenyTSConnections -eq 0)
        [void]$findings.Add(('RDP habilitado: {0}' -f $rdpOn))
        if ($rdpOn) {
            [void]$recs.Add('RDP habilitado: comprobar que NLA esta exigido, que el firewall no lo expone a internet y quien esta en el grupo de Escritorio Remoto. TechCare no lo cierra: podria dejar sin acceso al propio tecnico.')
        }
    } catch {
        [void]$findings.Add('No se pudo leer el estado de RDP: ' + $_.Exception.Message)
    }

    try {
        $session  = New-Object -ComObject Microsoft.Update.Session
        $searcher = $session.CreateUpdateSearcher()
        # BrowseOnly=0 excluye las actualizaciones opcionales que Windows no
        # ofrece por si solo, para no inflar la cuenta de parches pendientes.
        $pending = $searcher.Search("IsInstalled=0 and IsHidden=0 and Type='Software' and BrowseOnly=0")
        [void]$findings.Add(('Parches de software pendientes: {0}' -f $pending.Updates.Count))
        if ($pending.Updates.Count -gt 0) {
            if ($status -ne 'Red') { $status = 'Yellow' }
            [void]$recs.Add('Aplicar los parches pendientes con el modulo Update.')
        }
    } catch {
        [void]$findings.Add('No se pudo consultar los parches pendientes: ' + $_.Exception.Message)
    }

    if (-not $Apply) {
        [void]$recs.Add('Ejecutar con -Apply para aplicar solo las tres correcciones seguras: firewall, firmas de Defender y SMBv1.')
    }

    Write-Progress -Activity 'TechCare' -Status 'Revision de seguridad completada' -Completed
    return Add-TechCareResult -Module $mod -Status $status -Findings $findings.ToArray() -Actions $actions.ToArray() -Recommendations $recs.ToArray()
}