14 - Playbook Reproducción en Otro PC (para agente IA)

Propósito: playbook autocontenido que un agente IA (u operador humano) puede seguir para reproducir exactamente el mismo setup de monitorización Splunk en otro PC Windows 11 con Splunk Enterprise ya instalado localmente.

Prerequisites del PC destino:

  • Windows 10/11 x64
  • Splunk Enterprise ya instalado y corriendo (Splunkd service, puerto 8000 web)
  • PowerShell 5.1+
  • winget disponible
  • Acceso a administrador local (para UAC)
  • Acceso a Internet (para descargar UF, Sysmon config y apps)
  • Splunk admin credentials para REST API (verificar antes)

Tiempo estimado: 30-45 minutos (con descargas). Resultado: UF instalado + 14 índices win_* + Sysmon con SwiftOnSecurity + TA-Windows + CIM + SSE + hardening logging + artefactos persistidos.


Arquitectura objetivo

+----------------------------+        +-----------------------------------+
|   PC destino (Windows)     |        |   Splunk Enterprise (local)       |
|                            |        |                                   |
|  Sysmon64 + SwiftOnSec     |        |   Indexer + Web UI (8000)         |
|  WinEventLog (Security,    |        |   Mgmt (8089)                      |
|    PowerShell, Defender,   |        |   Receiving (9997)                |
|    Sysmon, WMI, RDP, ...)  |        |                                   |
|  Perfmon (CPU/Mem/Net/Dsk) |        |   Apps:                          |
|  FIM (hosts/lmhosts/GPO)   |        |   - SOC-Windows-Local (índices)  |
|       |                    |        |   - Splunk_TA_windows (parseo CIM)|
|       v                    |  TCP   |   - Splunk_SA_CIM (datamodels)    |
|  Splunk Universal Forwarder|------->|   - Splunk_Security_Essentials     |
|  (inputs.conf + outputs)   | 9997   |                                   |
+----------------------------+        +-----------------------------------+

FASE 0 — Verificación de prerequisitos

0.1 Confirmar Splunk Enterprise corriendo

# (NO admin)
Get-Service Splunkd | Format-Table Name,Status,StartType
foreach ($p in 8000,8089) {
    $t = Test-NetConnection -ComputerName 127.0.0.1 -Port $p -WarningAction SilentlyContinue
    "Puerto $p : $($t.TcpTestSucceeded)"
}

Esperado: Splunkd Running, puertos 8000 y 8089 True. Si no → abortar: el indexer no está listo.

0.2 Confirmar winget

winget --version
winget search Splunk.UniversalForwarder | Select-Object -First 5

Esperado: versión de winget + línea con Splunk.UniversalForwarder 10.x.x.

0.3 Verificar credenciales Splunk admin

Necesitas usuario + password de Splunk Web (http://127.0.0.1:8000). El agente debe pedirlo al usuario.

0.4 Preparar carpeta de trabajo

$base = "$env:TEMP\opencode\SplunkUF_Install"
New-Item -ItemType Directory -Path $base -Force | Out-Null

FASE 1 — Generar artefactos de configuración

Crear en $base estos 3 archivos (copiar literal):

1.1 outputs.conf

[tcpout]
defaultGroup = default-autolb-group
useACK = true
heartbeatFrequency = 30
maxQueueSize = 500KB
 
[tcpout:default-autolb-group]
server = 127.0.0.1:9997

CRÍTICO: useACK con K mayúscula. useAck provoca Invalid key y aborta el arranque. Ver 08 - Troubleshooting > 4. useACK-case-sensitive.

Si el indexer NO está en localhost, cambiar 127.0.0.1:9997 por <ip_indexer>:9997.

1.2 inputs.conf

## ===========================================================
## inputs.conf - Splunk Universal Forwarder (PC local analista SOC)
## Ruta destino: $SPLUNK_HOME\etc\apps\SOC-Windows-Local\local\inputs.conf
## ===========================================================
 
[default]
host = <HOSTNAME_DEL_PC_DESTINO>
 
## --- Windows Event Logs criticos para SOC -------------------------------
[WinEventLog://Security]
disabled = 0
index = win_security
renderXml = true
checkpointInterval = 5
start_from = oldest
current_only = 0
evt_resolve_ad_obj = 0
 
[WinEventLog://System]
disabled = 0
index = win_system
renderXml = true
evt_resolve_ad_obj = 0
 
[WinEventLog://Application]
disabled = 0
index = win_app
renderXml = true
evt_resolve_ad_obj = 0
 
[WinEventLog://Setup]
disabled = 0
index = win_setup
renderXml = true
 
[WinEventLog://Microsoft-Windows-Sysmon/Operational]
disabled = 0
index = win_sysmon
renderXml = true
checkpointInterval = 5
start_from = oldest
current_only = 0
evt_resolve_ad_obj = 0
 
[WinEventLog://Microsoft-Windows-PowerShell/Operational]
disabled = 0
index = win_powershell
renderXml = true
evt_resolve_ad_obj = 0
 
[WinEventLog://Microsoft-Windows-Windows Defender/Operational]
disabled = 0
index = win_defender
renderXml = true
 
[WinEventLog://Microsoft-Windows-WMI-Activity/Operational]
disabled = 0
index = win_wmi
renderXml = true
 
[WinEventLog://Microsoft-Windows-TerminalServices-LocalSessionManager/Operational]
disabled = 0
index = win_rdp
renderXml = true
 
[WinEventLog://Microsoft-Windows-TaskScheduler/Operational]
disabled = 0
index = win_tasks
renderXml = true
 
[WinEventLog://Microsoft-Windows-PrintService/Admin]
disabled = 0
index = win_print
renderXml = true
 
## --- Perfmon CPU/Memoria/Red (anomalias y baselines) -------------------
[perfmon://CPU]
interval = 60
counters = % Processor Time; % User Time; % Privileged Time; % Interrupt Time
instances = _Total
index = win_perfmon
disabled = 0
 
[perfmon://Memory]
interval = 60
counters = Available MBytes; Committed Bytes; % Committed Bytes In Use; Pool Paged Bytes; Pool Nonpaged Bytes
index = win_perfmon
disabled = 0
 
[perfmon://Network]
interval = 60
object = Network Interface
counters = Bytes Total/sec; Packets/sec; Packets Received Errors; Output Queue Length
instances = *
index = win_perfmon
disabled = 0
 
[perfmon://LogicalDisk]
interval = 60
counters = % Free Space; Avg. Disk sec/Transfer; Disk Read Bytes/sec; Disk Write Bytes/sec
instances = *
index = win_perfmon
disabled = 0
 
## --- Endpoint script: estado del UF ------------------------------------
[script://$SPLUNK_HOME\bin\scripts\win-listen.bat]
disabled = 1
interval = 300
index = win_status
sourcetype = script:win-listen
 
## --- File Integrity Monitoring (rutas criticas) -----------------------
[monitor://C:\Windows\System32\drivers\etc\hosts]
disabled = 0
index = win_fim
 
[monitor://C:\Windows\System32\drivers\etc\lmhosts]
disabled = 0
index = win_fim
 
[monitor://C:\Windows\System32\GroupPolicy]
disabled = 0
index = win_fim
recursive = true

Cambios respecto al original de este PC:

  1. host = <HOSTNAME_DEL_PC_DESTINO> → sustituir por el hostname real (obtenible con hostname o $env:COMPUTERNAME)
  2. evt_resolve_ad_obj = 0 añadido a todas las stanzas WinEventLog → evita errores EvtDC::bind (1355) en hosts workgroup
  3. win-listen.bat script stanza disabled = 1 (el script no existe por defecto y genera error ExecProcessor)

Ver detalles en 08 - Troubleshooting.

1.3 (Opcional) reusar artefactos del vault

Si tienes acceso al vault de Obsidian, los archivos inputs.conf, outputs.conf están en:

<VAULT>\Destino\SPLUNK\Monitorizacion PC Local\artefactos\

Copia outputs.conf y inputs.conf a $base, y ajusta:

  • outputs.conf: IP del indexer si no es localhost.
  • inputs.conf: valor de host = con el hostname del nuevo PC.

FASE 2 — Script de instalación principal (admin)

Crear $base\install_all.ps1 con este contenido. Se ejecuta como administrador (UAC):

## install_all.ps1 - Instalacion completa UF + Indexer config + Hardening + Sysmon + Apps
#Requires -Version 5.1
$ErrorActionPreference = 'Continue'
$base = "$env:TEMP\opencode\SplunkUF_Install"
$log  = Join-Path $base 'install_all.log'
Start-Transcript -Path $log -Force | Out-Null
try {
    $splunkHome = 'C:\Program Files\Splunk'
    $ufHome     = 'C:\Program Files\SplunkUniversalForwarder'
    $idxBin     = Join-Path $splunkHome 'bin\splunk.exe'
    $ufBin      = Join-Path $ufHome 'bin\splunk.exe'
    $hostName   = $env:COMPUTERNAME
 
    function W-Step($m){ Write-Host "`n==== $m ====" -ForegroundColor Cyan }
    function Copy-Cfg($src,$dst){
        $d = Split-Path $dst -Parent
        if (!(Test-Path $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null }
        Copy-Item -LiteralPath $src -Destination $dst -Force
    }
 
    ## 1. Instalar UF via winget --------------------------------
    W-Step '1/12 Instalando Splunk Universal Forwarder (winget)'
    $prev = winget list --id Splunk.UniversalForwarder 2>$null
    if ($LASTEXITCODE -eq 0 -and $prev -match 'Splunk.UniversalForwarder') {
        Write-Host 'UF ya instalado; continuando' -ForegroundColor Yellow
    } else {
        winget install --exact --id Splunk.UniversalForwarder `
            --silent --accept-package-agreements --accept-source-agreements `
            --disable-interactivity
    }
    Start-Sleep -Seconds 5
 
    ## 2. outputs.conf en UF ------------------------------------
    W-Step '2/12 Copiando outputs.conf a UF/etc/system/local'
    Copy-Cfg (Join-Path $base 'outputs.conf') (Join-Path $ufHome 'etc\system\local\outputs.conf')
 
    ## 3. App SOC-Windows-Local en UF ---------------------------
    W-Step '3/12 Creando app SOC-Windows-Local en UF'
    $appUf = Join-Path $ufHome 'etc\apps\SOC-Windows-Local'
    New-Item -ItemType Directory -Path $appUf -Force | Out-Null
    New-Item -ItemType Directory -Path (Join-Path $appUf 'local') -Force | Out-Null
    # Sustituir host en inputs.conf
    $inputsContent = Get-Content (Join-Path $base 'inputs.conf') -Raw
    $inputsContent = $inputsContent -replace 'host = <HOSTNAME_DEL_PC_DESTINO>', "host = $hostName"
    Set-Content -Path (Join-Path $appUf 'local\inputs.conf') -Value $inputsContent -Encoding UTF8
 
    @'
[app]
author = SOC Local
description = Inputs Windows para monitorizacion de PC analista ciberseguridad
version = 1.0.0
'@ | Set-Content -Path (Join-Path $appUf 'app.conf') -Encoding UTF8
 
    ## 4. App SOC-Windows-Local en INDEXER -----------------------
    W-Step '4/12 Creando app SOC-Windows-Local en Indexer con indexes.conf'
    $idxApp = Join-Path $splunkHome 'etc\apps\SOC-Windows-Local'
    New-Item -ItemType Directory -Path $idxApp -Force | Out-Null
    New-Item -ItemType Directory -Path (Join-Path $idxApp 'local') -Force | Out-Null
    New-Item -ItemType Directory -Path (Join-Path $idxApp 'metadata') -Force | Out-Null
 
    $indexes = 'win_security','win_system','win_app','win_setup','win_sysmon',
               'win_powershell','win_defender','win_wmi','win_rdp','win_tasks',
               'win_print','win_perfmon','win_fim','win_status'
    $tpl = @'
[{NAME}]
homePath   = $SPLUNK_DB/{NAME}/db
coldPath   = $SPLUNK_DB/{NAME}/colddb
thawedPath = $SPLUNK_DB/{NAME}/thaweddb
maxTotalDataSizeMB = 5000
 
'@
    $content = ''
    foreach ($i in $indexes) { $content += ($tpl -replace '\{NAME\}',$i) }
    Set-Content -Path (Join-Path $idxApp 'local\indexes.conf') -Value $content -Encoding UTF8
 
    @'
[app]
author = SOC Local
description = Indices Windows para monitorizacion de PC analista ciberseguridad
version = 1.0.0
'@ | Set-Content -Path (Join-Path $idxApp 'app.conf') -Encoding UTF8
 
    @'
[]
access = read : [ * ], write : [ admin ]
export = system
'@ | Set-Content -Path (Join-Path $idxApp 'metadata\default.meta') -Encoding UTF8
 
    ## 5. Habilitar receiving 9997 en Indexer --------------------
    W-Step '5/12 Habilitando splunktcp://9997'
    $idxInputs = Join-Path $splunkHome 'etc\system\local\inputs.conf'
    $needAdd = $true
    if (Test-Path $idxInputs) {
        $c = Get-Content $idxInputs -Raw
        if ($c -match '\[splunktcp://9997\]') { $needAdd = $false }
    }
    if ($needAdd) {
        $newBlock = "`r`n[splunktcp://9997]`r`n`r`n"
        if (Test-Path $idxInputs) { Add-Content -Path $idxInputs -Value $newBlock -Encoding UTF8 }
        else { Set-Content -Path $idxInputs -Value $newBlock -Encoding UTF8 }
        'Anadido [splunktcp://9997]'
    } else { '[splunktcp://9997] ya presente' }
 
    ## 6. Hardening logging (PS 4104 + Process Command Line) ---
    W-Step '6/12 Hardening logging SOC'
    $psP = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell'
    New-Item -Path "$psP\ScriptBlockLogging" -Force | Out-Null
    Set-ItemProperty -Path "$psP\ScriptBlockLogging" -Name 'EnableScriptBlockLogging' -Value 1 -Type DWord
    New-Item -Path "$psP\ModuleLogging" -Force | Out-Null
    Set-ItemProperty -Path "$psP\ModuleLogging" -Name 'EnableModuleLogging' -Value 1 -Type DWord
    Set-ItemProperty -Path "$psP\ModuleLogging" -Name 'ModuleNames' -Value '*' -Type MultiString
    $apc = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\AuditProcessCreation'
    New-Item -Path $apc -Force | Out-Null
    Set-ItemProperty -Path $apc -Name 'IncludeCommandLine' -Value 1 -Type DWord
    auditpol /set /subcategory:"Process Creation" /success:enable /failure:enable | Out-Null
    auditpol /set /subcategory:"Logon" /success:enable /failure:enable | Out-Null
    auditpol /set /subcategory:"Logoff" /success:enable | Out-Null
    auditpol /set /subcategory:"Special Logon" /success:enable | Out-Null
    'Politicas y auditpol actualizados'
 
    ## 7. Sysmon: instalar o actualizar con SwiftOnSecurity -----
    W-Step '7/12 Sysmon + SwiftOnSecurity'
    $sysmonDir = 'C:\Sysmon'
    if (!(Test-Path $sysmonDir)) { New-Item -ItemType Directory -Path $sysmonDir -Force | Out-Null }
    $svc = Get-Service Sysmon64 -ErrorAction SilentlyContinue
 
    # Descargar SwiftOnSecurity
    $swiftUrl = 'https://raw.githubusercontent.com/SwiftOnSecurity/sysmon-config/master/sysmonconfig-export.xml'
    $swiftPath = Join-Path $sysmonDir 'sysmonconfig-swiftonsecurity.xml'
    try {
        Invoke-WebRequest -Uri $swiftUrl -OutFile $swiftPath -UseBasicParsing -ErrorAction Stop
    } catch {
        & curl.exe -L -o $swiftPath $swiftUrl 2>&1 | Out-Null
    }
    "SwiftOnSecurity descargado: $(Test-Path $swiftPath) ($((Get-Item $swiftPath -ErrorAction SilentlyContinue).Length) bytes)"
 
    # Validar XML
    try {
        [xml]$xml = Get-Content $swiftPath -Raw
        "XML valido, RuleGroups: $($xml.SelectNodes('//RuleGroup').Count)"
    } catch {
        throw "XML invalido: $($_.Exception.Message)"
    }
 
    # Descargar Sysmon si no esta instalado
    if (-not $svc) {
        W-Step '7b/12 Descargando e instalando Sysmon64'
        $sysmonUrl = 'https://download.sysinternals.com/files/Sysmon.zip'
        $zipPath = Join-Path $sysmonDir 'Sysmon.zip'
        Invoke-WebRequest -Uri $sysmonUrl -OutFile $zipPath -UseBasicParsing
        Expand-Archive -Path $zipPath -DestinationPath $sysmonDir -Force
        $sysmonExe = Join-Path $sysmonDir 'Sysmon64.exe'
        & $sysmonExe -accepteula -i $swiftPath 2>&1 | Out-Host
        Start-Sleep -Seconds 3
    } else {
        # Aplicar config al Sysmon existente
        & 'C:\Windows\Sysmon64.exe' -accepteula -c $swiftPath 2>&1 | Out-Host
        Start-Sleep -Seconds 3
    }
    "Sysmon64 status: $((Get-Service Sysmon64 -ErrorAction SilentlyContinue).Status)"
 
    ## 8. FIX CRÍTICO: SDDL canal Sysmon -----------------------
    W-Step '8/12 Modificar SDDL canal Sysmon (grant Everyone read)'
    $newSddl = 'O:BAG:SYD:(A;;0xf0007;;;SY)(A;;0x7;;;BA)(A;;0x1;;;BO)(A;;0x1;;;SO)(A;;0x1;;;S-1-5-32-573)(A;;0x1;;;WD)'
    wevtutil set-log 'Microsoft-Windows-Sysmon/Operational' /ca:$newSddl 2>&1 | Out-Null
    'SDDL actualizado'
 
    ## 9. Verificar apps preinstaladas problematicas ------------
    W-Step '9/12 Verificar apps con props.conf invalido'
    $apps = Get-ChildItem (Join-Path $splunkHome 'etc\apps') -Directory
    foreach ($a in $apps) {
        $p = Join-Path $a.FullName 'default\props.conf'
        if (Test-Path -LiteralPath $p) {
            $lines = Get-Content -LiteralPath $p -ErrorAction SilentlyContinue
            # Buscar lineas con EXTRACT-<name> sin '=' despues
            $bad = $lines | Where-Object { $_ -match '^\s*EXTRACT-[A-Za-z0-9_\-]+\s+[^=\s]' -and $_ -notmatch '=' }
            if ($bad) {
                $bak = "$p.bak"
                if (Test-Path -LiteralPath $bak) { Remove-Item -LiteralPath $bak -Force }
                Move-Item -LiteralPath $p -Destination $bak -Force
                "Renombrado (props invalido): $($a.Name)\default\props.conf -> .bak"
            }
        }
    }
 
    ## 10. Instalar Splunk_TA_windows --------------------------
    W-Step '10/12 Instalar Splunk Add-on for Windows'
    $taInstalled = Test-Path (Join-Path $splunkHome 'etc\apps\Splunk_TA_windows')
    if (-not $taInstalled) {
        # Buscar .spl o .tgz en Downloads
        $taFile = Get-ChildItem "$env:USERPROFILE\Downloads" -Filter '*add-on-for-microsoft-windows*' -ErrorAction SilentlyContinue | Select-Object -First 1
        if (-not $taFile) {
            $taFile = Get-ChildItem "$env:USERPROFILE\Downloads" -Filter '*TA_windows*' -ErrorAction SilentlyContinue | Select-Object -First 1
        }
        if ($taFile) {
            & tar -xzf $taFile.FullName -C (Join-Path $splunkHome 'etc\apps') 2>&1 | Out-Null
            "TA-Windows instalado desde: $($taFile.FullName)"
        } else {
            'WARN: No se encontro Splunk_TA_windows en Downloads. Descargar manualmente de https://splunkbase.splunk.com/app/742'
        }
    } else { 'TA-Windows ya instalado' }
 
    ## 11. Instalar CIM + Splunk Security Essentials ------------
    W-Step '11/12 Instalar CIM y Splunk Security Essentials'
    # CIM
    $cimInstalled = Test-Path (Join-Path $splunkHome 'etc\apps\Splunk_SA_CIM')
    if (-not $cimInstalled) {
        $cimFile = Get-ChildItem "$env:USERPROFILE\Downloads" -Filter '*common-information-model*' -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($cimFile) {
            & tar -xzf $cimFile.FullName -C (Join-Path $splunkHome 'etc\apps') 2>&1 | Out-Null
            "CIM instalado desde: $($cimFile.FullName)"
        } else {
            'WARN: No se encontro CIM en Downloads. Descargar manualmente de https://splunkbase.splunk.com/app/1621'
        }
    } else { 'CIM ya instalado' }
 
    # SSE
    $sseInstalled = Test-Path (Join-Path $splunkHome 'etc\apps\Splunk_Security_Essentials')
    if (-not $sseInstalled) {
        $sseFile = Get-ChildItem "$env:USERPROFILE\Downloads" -Filter '*security-essentials*' -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($sseFile) {
            # Podria ser .tgz o carpeta extraida
            if ($sseFile.PSIsContainer) {
                $sseApp = Get-ChildItem $sseFile.FullName -Directory -Filter 'Splunk_Security_Essentials' -ErrorAction SilentlyContinue | Select-Object -First 1
                if ($sseApp) {
                    Copy-Item $sseApp.FullName (Join-Path $splunkHome 'etc\apps\Splunk_Security_Essentials') -Recurse -Force
                }
            } else {
                & tar -xzf $sseFile.FullName -C (Join-Path $splunkHome 'etc\apps') 2>&1 | Out-Null
            }
            "SSE instalado desde: $($sseFile.FullName)"
        } else {
            'WARN: No se encontro SSE en Downloads. Descargar manualmente de https://splunkbase.splunk.com/app/3522'
        }
    } else { 'SSE ya instalado' }
 
    ## 12. Restart Indexer + UF ----------------------------------
    W-Step '12/12 Restart Indexer y UF'
    & $idxBin restart 2>&1 | Out-Host
    Start-Sleep -Seconds 15
    if (Test-Path $ufBin) { & $ufBin restart 2>&1 | Out-Host }
    Start-Sleep -Seconds 5
    Get-Service Splunkd,SplunkForwarder | Format-Table Name,Status,StartType -AutoSize
 
    W-Step 'INSTALACION COMPLETA'
    Write-Host 'Siguiente: ejecutar verify_install.ps1 (NO admin) para verificar llegada de datos'
} catch {
    Write-Host "ERROR: $($_.Exception.Message)" -ForegroundColor Red
    Write-Host $_.ScriptStackTrace
} finally { Stop-Transcript | Out-Null }

FASE 3 — Script de verificación (NO admin)

Crear $base\verify_install.ps1:

## verify_install.ps1 - Verifica llegada de datos via REST API (NO admin)
[Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
[System.Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}
 
# Pedir credenciales Splunk
$spUser = Read-Host "Usuario Splunk admin (ej: admin)"
$spPass = Read-Host "Password Splunk" -AsSecureString
$spPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto(
    [Runtime.InteropServices.Marshal]::SecureStringToBSTR($spPass))
$auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${spUser}:${spPass}"))
$h = @{Authorization = ("Basic " + $auth)}
 
function Invoke-SplunkSearch($spl, $earliest = '-24h') {
    $body = "search=" + [Uri]::EscapeDataString("search $spl") +
            "&earliest_time=$earliest&latest_time=now&output_mode=json"
    Invoke-RestMethod -Uri 'https://127.0.0.1:8089/services/search/jobs/export' `
        -Method Post -Headers $h -ContentType 'application/x-www-form-urlencoded' `
        -Body $body -TimeoutSec 120
}
 
# 1. Servicios
Write-Host "`n==== 1. Servicios ====" -ForegroundColor Cyan
Get-Service Splunkd,SplunkForwarder | Format-Table Name,Status -AutoSize
 
# 2. Puertos
Write-Host "==== 2. Puertos ====" -ForegroundColor Cyan
foreach ($p in 8000,8089,9997) {
    $t = Test-NetConnection 127.0.0.1 -Port $p -WarningAction SilentlyContinue
    "Puerto $p : $($t.TcpTestSucceeded)"
}
 
# 3. Apps instaladas
Write-Host "`n==== 3. Apps instaladas ====" -ForegroundColor Cyan
$wc = New-Object System.Net.WebClient
$wc.Headers.Add('Authorization', "Basic $auth")
$r = $wc.DownloadString('https://127.0.0.1:8089/services/apps/local?count=-1')
($r -split "`n") | Select-String '(?i)<title>' | ForEach-Object {
    ($_.Line -replace '<[^>]+>','').Trim()
} | Where-Object { $_ -match 'Secur|CIM|TA_W|SOC-Windows|Essentials' }
 
# 4. Llegada por index
Write-Host "`n==== 4. Eventos por index win_* ====" -ForegroundColor Cyan
$q = '| tstats count where index=win_* by index | sort -count'
$res = Invoke-SplunkSearch $q
$res | Format-Table -AutoSize
 
# 5. Sysmon EIDs
Write-Host "`n==== 5. Sysmon EIDs recibidos ====" -ForegroundColor Cyan
$q = 'index=win_sysmon | stats count by EventID | sort -count'
$res = Invoke-SplunkSearch $q
$res | Format-Table -AutoSize
 
# 6. Last events
Write-Host "`n==== 6. Ultimos eventos por index ====" -ForegroundColor Cyan
$q = '| tstats latest(_time) as last where index=win_* by index,sourcetype | convert ctime(last) timeformat="%Y-%m-%d %H:%M:%S" | sort -last'
$res = Invoke-SplunkSearch $q
$res | Format-Table -AutoSize
 
Write-Host "`n==== VERIFICACION COMPLETA ====" -ForegroundColor Green

FASE 4 — Ejecución paso a paso

4.1 Generar artefactos (FASE 1)

En una sesión NO admin de PowerShell:

$base = "$env:TEMP\opencode\SplunkUF_Install"
New-Item -ItemType Directory -Path $base -Force | Out-Null
 
# Sustituir <HOSTNAME_DEL_PC_DESTINO> en inputs.conf placeholder
# Crear outputs.conf, inputs.conf y install_all.ps1 con el contenido de FASE 1 y 2

4.2 Descargar apps manualmente (si no están en Downloads)

El agente NO puede descargar de splunkbase (requiere login). El usuario debe descargar:

AppURLGuardar como
Splunk Add-on for Windowshttps://splunkbase.splunk.com/app/742Downloads\splunk-add-on-for-microsoft-windows_*.spl
Splunk CIMhttps://splunkbase.splunk.com/app/1621Downloads\splunk-common-information-model-*.tgz
Splunk Security Essentialshttps://splunkbase.splunk.com/app/3522Downloads\splunk-security-essentials-*.tgz

⚠ El script install_all.ps1 busca estos archivos automáticamente en Downloads con patrones wildcard. Si no los encuentra, continua con WARN.

4.3 Ejecutar instalación (admin)

# (NO admin) Lanzar UAC
Start-Process -FilePath 'powershell.exe' `
    -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-File',"$base\install_all.ps1") `
    -Verb RunAs -Wait

Aprobar UAC cuando aparezca.

4.4 Verificar (NO admin)

powershell -NoProfile -ExecutionPolicy Bypass -File "$base\verify_install.ps1"

Introducir credenciales Splunk cuando pida. Esperado:

  • Servicios Splunkd + SplunkForwarder Running
  • Puertos 8000/8089/9997 True
  • Apps: Splunk_TA_windows, Splunk_SA_CIM, Splunk_Security_Essentials, SOC-Windows-Local
  • win_security, win_sysmon, win_powershell con count > 0
  • Sysmon EIDs: 1, 3, 22, 11, 13, 5
  • Ultimos timestamps recientes (minutos)

4.5 Activar CIM datamodel acceleration (manual, Splunk Web)

  1. http://127.0.0.1:8000Settings → Data Models
  2. Activar acceleration en:
    • Authentication
    • Endpoint.Processes
    • Endpoint.NetworkSessions
    • Endpoint.Services
    • Performance
  3. Summary range: 1 year (o 30 days si disco limitado)

4.6 Persistir artefactos (opcional)

Si el PC destino tiene carpeta de Obsidian:

$vaultArte = '<RUTA_VAULT_OBSIDIAN>\Destino\SPLUNK\Monitorizacion PC Local\artefactos'
if (!(Test-Path $vaultArte)) { New-Item -ItemType Directory -Path $vaultArte -Force | Out-Null }
Get-ChildItem $base -File | Copy-Item -Destination $vaultArte -Force

FASE 5 — Troubleshooting conocido

Si algo falla, consultar 08 - Troubleshooting (aplica los mismos errores). Resumen:

ErrorCausaFix
Problem parsing indexes.conf: homePath must be absolute$SPLUNK_DB expandido por PowerShellUsar here-string single-quoted: ver FASE 2 sección 4
Invalid key ... useAck (value: true)Case-sensitive, debe ser useACKoutputs.conf: useACK = true (K mayúscula)
Cannot parse into key-value pair: EXTRACT-srcApp preinstalada con props.conf roto (botsv3)Renombrar default\props.conf a .bak en app culpable
Could not subscribe to Windows Event Log channel 'Microsoft-Windows-Sysmon/Operational': errorCode=5UF no tiene acceso al canal Sysmon (no es SYSTEM)Modificar SDDL con wevtutil set-log (FASE 2 sección 8)
EvtDC::bind: Failed to get domain controller name (1355)Host workgroup, sin ADAñadir evt_resolve_ad_obj = 0 en stanzas WinEventLog
win_perfmon = 0 eventosError PDH counter en algunos WindowsNo crítico, revisar contadores disponibles

FASE 6 — Post-instalación

6.1 Activar detecciones en SSE

  1. Abrir Splunk Security Essentials app.
  2. Showcased Content → MITRE ATT&CK → explorar detecciones.
  3. “Open in Search” en cada detection para adaptar a win_* indexes.

6.2 Crear dashboards SOC

Usar queries de 11 - Busquedas SOC y Casos de Uso como base. Save As → Dashboard Panel.

6.3 Configurar alertas

Usar queries de la sección 15 de Busquedas SOC. Save As → Alert con cron y action (email/webhook).

6.4 Documentación

Este playbook + los 13 documentos del vault son toda la base de conocimiento necesaria. El nuevo PC puede copiar la carpeta Destino\SPLUNK\Monitorización PC Local\ completa al suya.


Checklist final

  • Splunk Enterprise corriendo (indexer)
  • UF instalado y Running
  • Puerto 9997 escuchando en indexer
  • App SOC-Windows-Local en UF e indexer
  • 14 índices win_* creados
  • Sysmon con SwiftOnSecurity
  • SDDL canal Sysmon modificado (Everyone read)
  • Splunk_TA_windows instalado
  • Splunk_SA_CIM instalado
  • Splunk_Security_Essentials instalado
  • PowerShell ScriptBlock Logging (4104) activado
  • Process Creation audit + CommandLine activado
  • Eventos llegando (verify_install.ps1 todos los counts > 0 salvo perfmon/FIM)
  • CIM datamodels acceleration activada
  • Artefactos copiados a carpeta persistente

Notas para el agente IA que reproduzca este playbook

  1. Leer antes todo el documento y los documentos referenciados del vault (08 - Troubleshooting, Anexo A - inputs.conf, Anexo B - outputs.conf, Anexo C - indexes.conf).
  2. No asumir credenciales Splunk por defecto — pedirlas al usuario.
  3. No omitir el paso SDDL (FASE 2 sección 8) — sin él, win_sysmon queda en 0 eventos silenciosamente.
  4. No omitir evt_resolve_ad_obj = 0 en hosts workgroup — causa errores 1355 en logs.
  5. Renombrar props.conf inválidos en apps preinstaladas antes de restart Splunkd — si no, Splunkd no arranca.
  6. Validar useACK casing antes de copiar outputs.conf — useAck aborta el UF.
  7. Verificar llegada de datos con verify_install.ps1 al final — no asumir que funciona sin confirmar.
  8. Documentar cada paso en log/transcript como se hace en este playbook.

Volver a: 00 - MOC Monitorización PC Local | Ver también: 08 - Troubleshooting | 10 - Artefactos y Scripts | 12 - Apps Complementarias y Post-Instalación