Manual Completo de PowerShell

1. Introducción a PowerShell

1.1 ¿Qué es PowerShell?

PowerShell es un shell de comandos y lenguaje de scripting desarrollado por Microsoft, construido sobre .NET Framework/.NET Core. Combina la flexibilidad de un lenguaje de scripting con la potencia de una herramienta de administración del sistema.

1.2 Versiones de PowerShell

Versión Año Características Principales
PowerShell 1.0 2006 Versión inicial, solo para Windows
PowerShell 2.0 2009 Remoting, módulos, funciones avanzadas
PowerShell 3.0 2012 Workflows, mejoras en remoting, ISE mejorado
PowerShell 5.0 2016 Classes, .NET Core support, mejor manejo de módulos
PowerShell 7.x 2020+ Multiplataforma, mejor rendimiento, nuevas características

1.3 PowerShell vs CMD

# Comparación de comandos básicos

# CMD (Command Prompt)
dir            # Listar archivos
ipconfig       # Configuración de red
netstat -ano   # Conexiones de red

# PowerShell equivalente
Get-ChildItem          # Listar archivos
Get-NetIPConfiguration # Configuración de red
Get-NetTCPConnection   # Conexiones de red

2. Fundamentos de PowerShell

2.1 Comandos Básicos

# Ayuda y descubrimiento
Get-Help Get-Process       # Mostrar ayuda para un cmdlet
Get-Command -Verb Get     # Listar todos los cmdlets que empiezan con Get
Update-Help               # Actualizar la ayuda local

# Sistema de archivos
Set-Location C:\          # Cambiar directorio (cd)
Get-ChildItem             # Listar contenido (dir/ls)
New-Item -ItemType File -Name "test.txt"  # Crear archivo
Remove-Item test.txt      # Eliminar archivo

# Procesos y servicios
Get-Process              # Listar procesos
Stop-Process -Name notepad  # Detener proceso
Get-Service              # Listar servicios
Start-Service -Name "Spooler"  # Iniciar servicio

2.2 Sintaxis de Cmdlets

Los cmdlets siguen el formato Verbo-Sustantivo (por ejemplo, Get-Process). Los verbos comunes incluyen:

Nota: PowerShell es sensible a mayúsculas/minúsculas en nombres de variables y archivos, pero no en cmdlets.

3. Cmdlets Esenciales

3.1 Manipulación de Archivos

# Listar archivos con filtrado
Get-ChildItem -Path C:\Windows -Filter *.log -Recurse -File

# Leer y escribir archivos
Get-Content -Path "C:\logs\app.log" -Tail 10  # Últimas 10 líneas
Set-Content -Path "output.txt" -Value "Texto de ejemplo"
Add-Content -Path "log.txt" -Value "$(Get-Date) - Evento ocurrido"

# Copiar y mover archivos
Copy-Item -Path "source.txt" -Destination "C:\backup\source.txt"
Move-Item -Path "old.txt" -Destination "new.txt"

# Comprimir archivos
Compress-Archive -Path "C:\data\*" -DestinationPath "C:\backup\data.zip"
Expand-Archive -Path "data.zip" -DestinationPath "C:\restore\"

3.2 Administración del Sistema

# Información del sistema
Get-ComputerInfo          # Información detallada del sistema
Get-CimInstance -ClassName Win32_OperatingSystem  # Info del OS
Get-Disk                  # Discos del sistema
Get-Volume                # Volúmenes

# Usuarios y grupos
Get-LocalUser            # Listar usuarios locales
New-LocalUser -Name "jdoe" -Description "Usuario de prueba"
Get-LocalGroup           # Listar grupos locales
Add-LocalGroupMember -Group "Administradores" -Member "jdoe"

# Event Logs
Get-EventLog -LogName System -Newest 10  # Últimos 10 eventos del sistema
Get-WinEvent -FilterHashtable @{LogName='Application'; Level=2}  # Eventos de error

4. Pipelines y Filtrado

4.1 Concepto de Pipeline

El pipeline (|) permite pasar la salida de un cmdlet como entrada al siguiente.

# Ejemplo básico de pipeline
Get-Process | Sort-Object -Property CPU -Descending | Select-Object -First 5

# Explicación:
# 1. Get-Process obtiene todos los procesos
# 2. Sort-Object los ordena por uso de CPU (descendente)
# 3. Select-Object toma los primeros 5

4.2 Cmdlets para Filtrado

# Where-Object para filtrar
Get-Service | Where-Object { $_.Status -eq 'Running' -and $_.StartType -eq 'Automatic' }

# Select-Object para seleccionar propiedades
Get-Process | Select-Object -Property Name, Id, CPU, WorkingSet

# Group-Object para agrupar
Get-ChildItem -File | Group-Object -Property Extension

# Sort-Object para ordenar
Get-Process | Sort-Object -Property WS -Descending | Select-Object -First 10

4.3 Operadores de Comparación

Operador Descripción Ejemplo
-eq Igual $x -eq 10
-ne No igual $x -ne 10
-gt Mayor que $x -gt 10
-lt Menor que $x -lt 10
-like Comparación con comodines (*) $name -like "j*"
-match Expresión regular $text -match "^[A-Z]"

5. Variables y Tipos de Datos

5.1 Variables Básicas

# Definir variables
$nombre = "Juan Pérez"
$edad = 35
$activo = $true
$fecha = Get-Date

# Usar variables
Write-Host "Nombre: $nombre, Edad: $edad"

# Variables especiales
$HOME       # Directorio home del usuario
$PWD        # Directorio actual
$PSVersionTable  # Versión de PowerShell
$_          # Variable automática (objeto actual en el pipeline)

5.2 Tipos de Datos

# Tipos explícitos
[int]$numero = 42
[string]$texto = "Hola mundo"
[datetime]$fecha = "2023-01-15"
[bool]$verdadero = $true

# Arrays
$frutas = @("Manzana", "Banana", "Naranja")
$frutas[1]  # Acceder al segundo elemento (Banana)

# Hashtables (diccionarios)
$persona = @{
    Nombre = "Carlos"
    Edad = 28
    Profesion = "Ingeniero"
}
$persona.Nombre  # Acceder a propiedad

5.3 Objetos Personalizados

# Crear objeto personalizado
$empleado = [PSCustomObject]@{
    ID = 1001
    Nombre = "María González"
    Departamento = "TI"
    Salario = 75000
    FechaContratacion = Get-Date "2020-05-15"
}

# Acceder a propiedades
$empleado.Nombre
$empleado.Departamento = "Finanzas"  # Modificar valor

# Exportar a CSV
$empleado | Export-Csv -Path "empleados.csv" -NoTypeInformation -Append

6. Operadores

6.1 Operadores Aritméticos

$a = 10
$b = 3

$a + $b   # Suma (13)
$a - $b   # Resta (7)
$a * $b   # Multiplicación (30)
$a / $b   # División (3.333...)
$a % $b   # Módulo (1)
++$a      # Incremento (11)
--$b      # Decremento (2)

6.2 Operadores de Asignación

$x = 5      # Asignación básica
$x += 3     # Equivalente a $x = $x + 3 (8)
$x -= 2     # $x = $x - 2 (6)
$x *= 4     # $x = $x * 4 (24)
$x /= 3     # $x = $x / 3 (8)

6.3 Operadores Lógicos

$true -and $false   # AND lógico (False)
$true -or $false   # OR lógico (True)
-not $true         # NOT lógico (False)
!$true             # Alternativa para NOT (False)

7. Estructuras de Control

7.1 Condicionales

# If-elseif-else
$temperatura = 25

if ($temperatura -lt 0) {
    Write-Host "Congelando"
} elseif ($temperatura -lt 20) {
    Write-Host "Fresco"
} elseif ($temperatura -lt 30) {
    Write-Host "Agradable"
} else {
    Write-Host "Caliente"
}

# Switch
$dia = (Get-Date).DayOfWeek

switch ($dia) {
    "Monday"    { Write-Host "Comienza la semana" }
    "Friday"    { Write-Host "¡Viernes!" }
    {$_ -in "Saturday","Sunday"} { Write-Host "Fin de semana" }
    default     { Write-Host "Día laboral" }
}

7.2 Bucles

# For
for ($i = 1; $i -le 5; $i++) {
    Write-Host "Iteración $i"
}

# Foreach (colección)
$servicios = Get-Service
foreach ($servicio in $servicios) {
    if ($servicio.Status -eq 'Running') {
        Write-Host "$($servicio.Name) está en ejecución"
    }
}

# While
$contador = 0
while ($contador -lt 3) {
    Write-Host "Contador: $contador"
    $contador++
}

# Do-While
$input = ""
do {
    $input = Read-Host "Ingrese 'salir' para terminar"
} while ($input -ne "salir")

8. Funciones y Scripts

8.1 Funciones Básicas

# Función simple
function Saludar {
    param(
        [string]$nombre = "Usuario"
    )
    Write-Host "Hola, $nombre!"
}

Saludar -nombre "Carlos"

# Función con valor de retorno
function Sumar {
    param(
        [double]$a,
        [double]$b
    )
    return $a + $b
}

$resultado = Sumar -a 5.5 -b 3.2
Write-Host "La suma es $resultado"

8.2 Funciones Avanzadas

# Función avanzada con validación
function Get-FileStats {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [ValidateScript({Test-Path $_ -PathType 'Leaf'})]
        [string]$FilePath,
        
        [ValidateSet('KB','MB','GB')]
        [string]$Unit = 'KB'
    )
    
    begin {
        Write-Verbose "Iniciando procesamiento de archivo"
    }
    
    process {
        $file = Get-Item $FilePath
        $size = switch ($Unit) {
            'KB' { $file.Length / 1KB }
            'MB' { $file.Length / 1MB }
            'GB' { $file.Length / 1GB }
        }
        
        [PSCustomObject]@{
            Name = $file.Name
            Path = $file.FullName
            Size = $size
            Unit = $Unit
            LastModified = $file.LastWriteTime
        }
    }
    
    end {
        Write-Verbose "Procesamiento completado"
    }
}

# Uso:
Get-FileStats -FilePath "C:\Windows\explorer.exe" -Unit MB -Verbose

8.3 Scripts y Parámetros

<#
.SYNOPSIS
    Script para copiar archivos con registro.
.DESCRIPTION
    Copia archivos de un origen a un destino y registra las operaciones.
.PARAMETER Source
    Directorio de origen.
.PARAMETER Destination
    Directorio de destino.
.PARAMETER LogFile
    Archivo de registro (opcional).
.EXAMPLE
    .\Copy-Files.ps1 -Source C:\temp -Destination D:\backup
#>

param(
    [Parameter(Mandatory=$true)]
    [string]$Source,
    
    [Parameter(Mandatory=$true)]
    [string]$Destination,
    
    [string]$LogFile = "copy_log.txt"
)

# Verificar directorios
if (-not (Test-Path $Source -PathType Container)) {
    Write-Error "El directorio de origen no existe"
    exit 1
}

if (-not (Test-Path $Destination -PathType Container)) {
    try {
        New-Item -ItemType Directory -Path $Destination | Out-Null
        Add-Content -Path $LogFile -Value "$(Get-Date) - Directorio creado: $Destination"
    } catch {
        Write-Error "No se pudo crear el directorio de destino"
        exit 1
    }
}

# Copiar archivos
Get-ChildItem -Path $Source -File | ForEach-Object {
    try {
        Copy-Item -Path $_.FullName -Destination $Destination -Force
        Add-Content -Path $LogFile -Value "$(Get-Date) - Copiado: $($_.Name)"
    } catch {
        Add-Content -Path $LogFile -Value "$(Get-Date) - ERROR copiando: $($_.Name)"
    }
}

Write-Host "Proceso completado. Ver registro en $LogFile"

9. Módulos y Snap-ins

9.1 Módulos Incorporados

# Listar módulos disponibles
Get-Module -ListAvailable

# Importar módulo
Import-Module NetTCPIP

# Ver cmdlets en un módulo
Get-Command -Module NetTCPIP

# Crear módulo simple
# Guardar como MyModule.psm1
function Get-Hello {
    Write-Host "Hola desde MyModule!"
}

Export-ModuleMember -Function Get-Hello

# Importar módulo personalizado
Import-Module .\MyModule.psm1
Get-Hello

9.2 Módulos de la Galería

# Configurar la Galería PowerShell (si es necesario)
Register-PSRepository -Default

# Buscar módulos
Find-Module -Name *Azure*

# Instalar módulo
Install-Module -Name Pester -Force -Scope CurrentUser

# Actualizar módulo
Update-Module -Name Pester

# Listar módulos instalados
Get-InstalledModule

10. Manejo de Errores

10.1 Try-Catch-Finally

try {
    # Código que podría generar errores
    $result = 10 / $null
    Write-Host "Resultado: $result"
}
catch [System.DivideByZeroException] {
    Write-Host "Error: División por cero"
    $_.Exception.Message  # Mensaje detallado del error
}
catch {
    # Captura cualquier otro error
    Write-Host "Error inesperado: $($_.Exception.GetType().Name)"
    Write-Host "Mensaje: $($_.Exception.Message)"
}
finally {
    # Siempre se ejecuta
    Write-Host "Operación completada (con o sin errores)"
}

10.2 Preferencias de Error

# Comportamiento por defecto (Continue)
$ErrorActionPreference = "Continue"  # Muestra error y continúa

# Opciones:
# - Stop: Termina la ejecución
# - SilentlyContinue: Omite el error sin mensaje
# - Inquire: Pregunta al usuario qué hacer
# - Ignore: Omite el error completamente

# Ejemplo:
$ErrorActionPreference = "Stop"
Get-Item "archivo_inexistente.txt"  # Terminará el script

10.3 Registro de Errores

# Variable $Error contiene los últimos errores
$Error[0]  # Último error
$Error.Clear()  # Limpiar registro de errores

# Crear registro personalizado
try {
    Get-Content "ruta_invalida.txt" -ErrorAction Stop
}
catch {
    $errorMsg = "$(Get-Date) - Error al leer archivo: $($_.Exception.Message)"
    Add-Content -Path "error_log.txt" -Value $errorMsg
    Write-Warning $errorMsg
}

11. Administración del Sistema

11.1 Administración de Procesos

# Listar procesos
Get-Process | Sort-Object CPU -Descending | Select-Object -First 10

# Detener proceso
Stop-Process -Name "notepad" -Force

# Iniciar proceso
Start-Process -FilePath "notepad.exe" -ArgumentList "C:\temp\notes.txt"

# Procesos remotos (necesita permisos)
Invoke-Command -ComputerName Server01 -ScriptBlock { Get-Process }

11.2 Administración de Servicios

# Listar servicios
Get-Service | Where-Object { $_.Status -eq 'Running' }

# Iniciar/detener servicio
Start-Service -Name "Spooler"
Stop-Service -Name "Spooler" -Force

# Configurar inicio automático
Set-Service -Name "Spooler" -StartupType Automatic

# Servicios remotos
Get-CimInstance -ClassName Win32_Service -ComputerName Server01 | 
    Where-Object { $_.State -eq 'Running' }

11.3 Administración de Event Logs

# Leer logs de eventos
Get-EventLog -LogName System -Newest 20
Get-WinEvent -LogName Application -MaxEvents 10 | 
    Where-Object { $_.Level -eq 2 }  # Eventos de error

# Crear nuevo log
New-EventLog -LogName "MyAppLog" -Source "MyApplication"

# Escribir en el log
Write-EventLog -LogName "MyAppLog" -Source "MyApplication" -EventId 1001 -EntryType Information -Message "Aplicación iniciada"

# Exportar eventos
Get-WinEvent -LogName System -MaxEvents 100 | Export-Csv -Path "system_events.csv"

12. PowerShell Remoting

12.1 Configuración Básica

# Habilitar PSRemoting (en el servidor)
Enable-PSRemoting -Force

# Verificar configuración
Get-PSSessionConfiguration

# Crear sesión remota
$session = New-PSSession -ComputerName "Server01" -Credential (Get-Credential)

# Ejecutar comando remoto
Invoke-Command -Session $session -ScriptBlock { Get-Service }

# Copiar archivos
Copy-Item -Path "C:\localfile.txt" -Destination "C:\remotefile.txt" -ToSession $session

# Cerrar sesión
Remove-PSSession $session

12.2 Sesiones Persistentes

# Crear sesión persistente
$session = New-PSSession -ComputerName "Server01" -Name "Maintenance"

# Entrar en la sesión (como SSH)
Enter-PSSession -Session $session
# Comandos interactivos...
Exit-PSSession

# Ejecutar múltiples comandos
Invoke-Command -Session $session -ScriptBlock {
    Get-Service | Where-Object { $_.Status -ne 'Running' } | Start-Service
    Get-EventLog -LogName System -Newest 10
}

# Listar sesiones activas
Get-PSSession

# Eliminar sesión
Remove-PSSession -Session $session

12.3 Configuración Avanzada

# Configurar sesión con opciones
$options = New-PSSessionOption -IdleTimeout 3600000 -OperationTimeout 300000
$cred = Get-Credential

$session = New-PSSession -ComputerName "Server01" -SessionOption $options -Credential $cred

# Configuración de punto final (JEA - Just Enough Administration)
Register-PSSessionConfiguration -Name "Limited" -RunAsCredential $cred -SecurityDescriptorSddl "O:NSG:BAD:P(A;;GA;;;BA)S:P(AU;FA;GA;;;WD)(AU;SA;GXGW;;;WD)"

# Conectar con punto final limitado
Enter-PSSession -ComputerName "Server01" -ConfigurationName "Limited"

13. Trabajo en Red

13.1 Cmdlets de Red

# Configuración de red
Get-NetIPConfiguration          # Configuración IP completa
Get-NetIPAddress -AddressFamily IPv4 | Select-Object InterfaceAlias, IPAddress
Get-NetRoute | Where-Object { $_.DestinationPrefix -eq '0.0.0.0/0' }  # Ruta por defecto

# DNS
Resolve-DnsName -Name "google.com" -Type A
Get-DnsClientServerAddress -AddressFamily IPv4

# Conexiones de red
Get-NetTCPConnection -State Established
Test-NetConnection -ComputerName "google.com" -Port 80

13.2 Transferencia de Archivos

# Descargar archivo
Invoke-WebRequest -Uri "https://example.com/file.zip" -OutFile "C:\temp\file.zip"

# Subir archivo (requiere configuración del servidor)
$headers = @{ Authorization = "Bearer token123" }
Invoke-WebRequest -Uri "https://api.example.com/upload" -Method Post -InFile "data.json" -Headers $headers

# FTP básico
$ftpRequest = [System.Net.FtpWebRequest]::Create("ftp://ftp.example.com/file.txt")
$ftpRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$ftpRequest.Credentials = New-Object System.Net.NetworkCredential("user", "pass")
$response = $ftpRequest.GetResponse()
$stream = $response.GetResponseStream()
# ... procesar stream

13.3 REST APIs

# GET request
$response = Invoke-RestMethod -Uri "https://api.github.com/users/octocat"
$response.name
$response.public_repos

# POST request con body JSON
$body = @{
    name  = "nuevo-repo"
    description = "Repositorio creado desde PowerShell"
    private = $false
} | ConvertTo-Json

$headers = @{
    Authorization = "token ghp_yourtokenhere"
    Accept = "application/vnd.github.v3+json"
}

Invoke-RestMethod -Uri "https://api.github.com/user/repos" -Method Post -Body $body -Headers $headers -ContentType "application/json"

14. Active Directory

14.1 Instalación del Módulo

# Instalar módulo AD (Windows)
Install-WindowsFeature -Name "RSAT-AD-PowerShell" -IncludeAllSubFeature

# Importar módulo
Import-Module ActiveDirectory

# Comandos disponibles
Get-Command -Module ActiveDirectory

14.2 Usuarios y Grupos

# Buscar usuarios
Get-ADUser -Filter { Name -like "j*" } -Properties *

# Crear usuario
New-ADUser -Name "Juan Perez" -GivenName "Juan" -Surname "Perez" `
    -SamAccountName "jperez" -UserPrincipalName "jperez@domain.com" `
    -Path "OU=Users,DC=domain,DC=com" -AccountPassword (ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force) -Enabled $true

# Modificar usuario
Set-ADUser -Identity "jperez" -Department "IT" -Title "SysAdmin"

# Grupos
Get-ADGroup -Filter * | Select-Object Name
Add-ADGroupMember -Identity "Administrators" -Members "jperez"

14.3 Consultas Avanzadas

# Usuarios inactivos
$inactiveDate = (Get-Date).AddDays(-90)
Get-ADUser -Filter { LastLogonDate -lt $inactiveDate -and Enabled -eq $true } -Properties LastLogonDate

# Buscar computadoras sin actualizar
Get-ADComputer -Filter { OperatingSystem -like "*Windows 7*" } -Properties OperatingSystem, LastLogonDate

# Exportar todos los usuarios a CSV
Get-ADUser -Filter * -Properties * | 
    Select-Object Name, SamAccountName, EmailAddress, Department, Title, Enabled | 
    Export-Csv -Path "all_users.csv" -NoTypeInformation

15. Scripting Avanzado

15.1 Clases en PowerShell

# Definición de clase
class Persona {
    [string]$Nombre
    [int]$Edad
    [string]$Profesion
    
    # Constructor
    Persona([string]$nombre, [int]$edad) {
        $this.Nombre = $nombre
        $this.Edad = $edad
    }
    
    # Método
    [string]Presentarse() {
        return "Hola, soy $($this.Nombre) y tengo $($this.Edad) años."
    }
}

# Uso de la clase
$persona1 = [Persona]::new("María", 30)
$persona1.Profesion = "Ingeniera"
Write-Host $persona1.Presentarse()

15.2 Trabajo con JSON

# Convertir a JSON
$data = @{
    nombre = "Carlos"
    edad = 35
    hobbies = @("programar", "leer", "correr")
    activo = $true
}

$json = $data | ConvertTo-Json -Depth 3
Write-Host $json

# Leer JSON
$jsonData = '{
    "nombre": "Ana",
    "puntuaciones": [85, 92, 78]
}'

$obj = $jsonData | ConvertFrom-Json
Write-Host "$($obj.nombre) tiene una puntuación promedio de $(($obj.puntuaciones | Measure-Object -Average).Average)"

15.3 Expresiones Regulares

# Coincidencia básica
$texto = "El teléfono es 555-1234 y el otro es 555-5678"
if ($texto -match "\d{3}-\d{4}") {
    Write-Host "Teléfono encontrado: $($matches[0])"
}

# Extraer múltiples coincidencias
$matches = [regex]::Matches($texto, "\d{3}-\d{4}")
$matches | ForEach-Object {
    Write-Host "Teléfono: $($_.Value)"
}

# Validación de email
function Test-Email {
    param([string]$email)
    $pattern = '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    return $email -match $pattern
}

Test-Email "usuario@dominio.com"  # True
Test-Email "invalido@dominio"     # False

16. Depuración y Perfilado

16.1 Depuración Básica

# Puntos de interrupción
Set-PSBreakpoint -Script .\script.ps1 -Line 10

# Depuración paso a paso
# Ejecutar script con:
# -Step: Paso a paso
# -Debug: Entrar en modo debug en breakpoints

# Variables de depuración
$DebugPreference = "Continue"  # Mostrar mensajes de debug
Write-Debug "Este mensaje se mostrará"

# Mensajes detallados
Write-Verbose "Información detallada" -Verbose

16.2 Perfilado de Rendimiento

# Medir tiempo de ejecución
Measure-Command {
    Get-ChildItem -Path C:\Windows -Recurse -File | Where-Object { $_.Length -gt 1MB }
}

# Analizar uso de memoria
$process = Get-Process -Id $PID
Write-Host "Memoria usada: $($process.WorkingSet / 1MB) MB"

# Script de perfilado
$scriptBlock = {
    # Código a medir
    1..10000 | ForEach-Object { [math]::Sqrt($_) }
}

$time = Measure-Command -Expression $scriptBlock
Write-Host "Tiempo total: $($time.TotalMilliseconds) ms"

17. Seguridad en PowerShell

17.1 Ejecución de Scripts

# Política de ejecución
Get-ExecutionPolicy  # Ver política actual
Set-ExecutionPolicy RemoteSigned  # Cambiar política

# Opciones:
# - Restricted: No scripts permitidos (default)
# - AllSigned: Solo scripts firmados
# - RemoteSigned: Scripts locales ok, remotos deben estar firmados
# - Unrestricted: Todos los scripts permitidos (no recomendado)

# Firmar scripts
$cert = Get-ChildItem -Path Cert:\CurrentUser\My -CodeSigningCert
Set-AuthenticodeSignature -FilePath .\script.ps1 -Certificate $cert

17.2 Credenciales Seguras

# Solicitar credenciales
$cred = Get-Credential

# Crear credenciales programáticamente (no seguro para producción)
$securePass = ConvertTo-SecureString "P@ssw0rd" -AsPlainText -Force
$cred = New-Object System.Management.Automation.PSCredential ("usuario", $securePass)

# Usar credenciales
Invoke-Command -ComputerName "Server01" -Credential $cred -ScriptBlock { Get-Service }

# Exportar/importar credenciales (solo para pruebas)
$cred.Password | ConvertFrom-SecureString | Set-Content "password.txt"
$securePass = Get-Content "password.txt" | ConvertTo-SecureString
$cred = New-Object System.Management.Automation.PSCredential ("usuario", $securePass)

17.3 Registro y Auditoría

# Habilitar registro de módulos (PowerShell 5.1+)
# En política de grupo o registro:
# HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging
# Habilitar y especificar módulos

# Registro de ejecución de scripts
# Habilitar en:
# HKLM\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging

# Transcribir sesiones
Start-Transcript -Path "C:\logs\powershell_$(Get-Date -Format 'yyyyMMdd').txt"
# Comandos interactivos...
Stop-Transcript

18. Ejemplos Prácticos

18.1 Monitor de Sistema

# Monitor básico de recursos
function Get-SystemMonitor {
    [CmdletBinding()]
    param(
        [int]$Interval = 5,
        [int]$Duration = 60
    )
    
    $endTime = (Get-Date).AddSeconds($Duration)
    $counter = 0
    
    while ((Get-Date) -lt $endTime) {
        $counter++
        $cpuUsage = (Get-Counter '\Processor(_Total)\% Processor Time').CounterSamples.CookedValue
        $memUsage = (Get-Counter '\Memory\% Committed Bytes In Use').CounterSamples.CookedValue
        $processCount = (Get-Process).Count
        
        Write-Host "$(Get-Date -Format 'HH:mm:ss') - CPU: $($cpuUsage.ToString('0.0'))% | Mem: $($memUsage.ToString('0.0'))% | Procesos: $processCount"
        
        if ($counter -lt ($Duration / $Interval)) {
            Start-Sleep -Seconds $Interval
        }
    }
}

# Uso:
Get-SystemMonitor -Interval 2 -Duration 30

18.2 Backup Automatizado

<#
.SYNOPSIS
    Script para backup de directorios.
.DESCRIPTION
    Crea backups comprimidos de directorios especificados.
.PARAMETER SourcePath
    Directorio(s) a respaldar.
.PARAMETER DestinationPath
    Directorio de destino para los backups.
.PARAMETER RetentionDays
    Número de días para retener backups (opcional).
#>
param(
    [Parameter(Mandatory=$true)]
    [string[]]$SourcePath,
    
    [Parameter(Mandatory=$true)]
    [string]$DestinationPath,
    
    [int]$RetentionDays = 0
)

$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupName = "Backup_$timestamp.zip"
$backupPath = Join-Path -Path $DestinationPath -ChildPath $backupName

# Verificar directorios de origen
foreach ($path in $SourcePath) {
    if (-not (Test-Path $path -PathType Container)) {
        Write-Error "Directorio no encontrado: $path"
        exit 1
    }
}

# Crear directorio de destino si no existe
if (-not (Test-Path $DestinationPath -PathType Container)) {
    try {
        New-Item -ItemType Directory -Path $DestinationPath | Out-Null
    } catch {
        Write-Error "No se pudo crear el directorio de destino: $_"
        exit 1
    }
}

# Crear backup
try {
    Compress-Archive -Path $SourcePath -DestinationPath $backupPath -CompressionLevel Optimal
    Write-Host "Backup creado exitosamente: $backupPath"
} catch {
    Write-Error "Error al crear backup: $_"
    exit 1
}

# Limpieza de backups antiguos
if ($RetentionDays -gt 0) {
    $cutoffDate = (Get-Date).AddDays(-$RetentionDays)
    Get-ChildItem -Path $DestinationPath -Filter "Backup_*.zip" | 
        Where-Object { $_.LastWriteTime -lt $cutoffDate } | 
        ForEach-Object {
            try {
                Remove-Item $_.FullName -Force
                Write-Host "Eliminado backup antiguo: $($_.Name)"
            } catch {
                Write-Warning "No se pudo eliminar $($_.Name): $_"
            }
        }
}

18.3 Reporte de Usuarios de AD

<#
.SYNOPSIS
    Genera reporte de usuarios de Active Directory.
.DESCRIPTION
    Crea un reporte HTML con información de usuarios de AD.
#>
param(
    [string]$OutputPath = "AD_User_Report.html",
    [string]$OU = "OU=Users,DC=domain,DC=com"
)

# Verificar módulo AD
if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
    Write-Error "Módulo Active Directory no disponible"
    exit 1
}

Import-Module ActiveDirectory

# Obtener usuarios
try {
    $users = Get-ADUser -Filter * -SearchBase $OU -Properties *
} catch {
    Write-Error "Error al obtener usuarios: $_"
    exit 1
}

# Generar HTML
$htmlHeader = @"
<!DOCTYPE html>
<html>
<head>
    <title>Reporte de Usuarios AD</title>
    <style>
        body { font-family: Arial, sans-serif; }
        table { border-collapse: collapse; width: 100%; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #0066cc; color: white; }
        tr:nth-child(even) { background-color: #f2f2f2; }
        .disabled { color: #999; }
    </style>
</head>
<body>
    <h1>Reporte de Usuarios de Active Directory</h1>
    <p>Generado: $(Get-Date)</p>
    <p>Total usuarios: $($users.Count)</p>
    <table>
        <tr>
            <th>Nombre</th>
            <th>Usuario</th>
            <th>Email</th>
            <th>Departamento</th>
            <th>Último inicio</th>
            <th>Estado</th>
        </tr>
"@

$htmlRows = $users | ForEach-Object {
    $lastLogon = if ($_.LastLogonDate) { $_.LastLogonDate.ToString("yyyy-MM-dd") } else { "Nunca" }
    $rowClass = if (-not $_.Enabled) { "class='disabled'" } else { "" }
    
    @"
        <tr $rowClass>
            <td>$($_.Name)</td>
            <td>$($_.SamAccountName)</td>
            <td>$($_.EmailAddress)</td>
            <td>$($_.Department)</td>
            <td>$lastLogon</td>
            <td>$(if ($_.Enabled) { "Activo" } else { "Desactivado" })</td>
        </tr>
"@
}

$htmlFooter = @"
    </table>
</body>
</html>
"@

# Guardar reporte
try {
    $htmlHeader + $htmlRows + $htmlFooter | Out-File -FilePath $OutputPath -Force
    Write-Host "Reporte generado exitosamente: $OutputPath"
} catch {
    Write-Error "Error al guardar reporte: $_"
    exit 1
}