Análisis de uso de aplicaciones IIS con Log Parser y PowerShell antes de migrar

Hace algunos días me topé con la tarea de realizar una migración de las aplicaciones de un IIS a otro servidor. El proceso inició con un análisis de uso de las aplicaciones, para identificar cuáles tenían muy poco o nulo uso en el último año y descartarlas de la migración.

De esta primera etapa voy a hablar en esta publicación.

Herramientas utilizadas

Para esta tarea utilicé las siguientes herramientas:

  • PowerShell — para obtener el listado de aplicaciones del IIS mediante el módulo WebAdministration y orquestar la ejecución del análisis.
  • Log Parser 2.2 — herramienta de consola gratuita de Microsoft que permite hacer queries de SQL sobre los archivos de log del IIS en formato W3C. La puedes descargar directamente del sitio de Microsoft buscando “Log Parser 2.2”.

Se requirieron privilegios de administrador y el módulo WebAdministration instalado en PowerShell.

El script de análisis

Para realizar el análisis me apoyé de un script de PowerShell cuya tarea es obtener el listado de aplicaciones del IIS y consultar, mediante un query de Log Parser, el estadístico de uso de cada una, clasificándolas en 3 niveles de riesgo según los días transcurridos desde su último acceso exitoso.

La consulta que se ejecuta sobre los logs filtra únicamente peticiones exitosas (código HTTP menor a 400) y excluye recursos estáticos como CSS, JavaScript, imágenes y documentos, de modo que el resultado refleja el uso real de páginas y endpoints, no el tráfico de archivos.

Los niveles de riesgo que manejo son:

NivelCondición
BAJOÚltimo acceso hace menos de 180 días
MEDIOÚltimo acceso entre 180 y 365 días
ALTOSin acceso exitoso en más de 365 días

El umbral de 365 días es configurable en la variable $thresholdDays al inicio del script.

El script quedó de la siguiente forma:

# RevisionIISv2.ps1
# ============================================================
# CONFIG
$logParserPath = "C:\Program Files (x86)\Log Parser 2.2\LogParser.exe"
$logRoot       = "C:\inetpub\logs\LogFiles"
$outputCsv     = "C:\Revision IIS\IIS_Auditoria_V2.csv"
$thresholdDays = 365

Import-Module WebAdministration
$resultados = @{}

foreach ($site in Get-Website) {
    $siteId  = $site.ID
    $logPath = Join-Path $logRoot "W3SVC$siteId"
    if (!(Test-Path $logPath)) { continue }

    $query = @"
SELECT cs-uri-stem,
       COUNT(*) AS Hits,
       MAX(TO_TIMESTAMP(date, time)) AS UltimoAcceso
FROM $logPath\*.log
WHERE sc-status < 400
  AND cs-uri-stem NOT LIKE '%.css'
  AND cs-uri-stem NOT LIKE '%.js'
  AND cs-uri-stem NOT LIKE '%.png'
  AND cs-uri-stem NOT LIKE '%.jpg'
  AND cs-uri-stem NOT LIKE '%.gif'
  AND cs-uri-stem NOT LIKE '%.ico'
  AND cs-uri-stem NOT LIKE '%.pdf'
  AND cs-uri-stem NOT LIKE '%.doc%'
  AND cs-uri-stem NOT LIKE '%.xls%'
  AND cs-uri-stem NOT LIKE '%health%'
GROUP BY cs-uri-stem
"@

    $tmpFile = [System.IO.Path]::GetTempFileName()
    & $logParserPath $query -i:IISW3C -o:CSV > $tmpFile
    $csv = Import-Csv $tmpFile
    Remove-Item $tmpFile

    foreach ($row in $csv) {
        if ([string]::IsNullOrWhiteSpace($row.UltimoAcceso)) { continue }
        try   { $fecha = [datetime]$row.UltimoAcceso }
        catch { continue }

        $diasSinUso = (New-TimeSpan -Start $fecha -End (Get-Date)).Days
        $appPath    = "/"
        if ($row.'cs-uri-stem' -match "^/([^/]+)") {
            $appPath = "/" + $matches[1]
        }
        $key = "$($site.Name)|$appPath"

        if ($resultados.ContainsKey($key)) {
            if ($fecha -gt $resultados[$key].UltimoAcceso) {
                $resultados[$key].UltimoAcceso = $fecha
                $resultados[$key].DiasSinUso   = $diasSinUso
                $resultados[$key].UltimaUrl    = $row.'cs-uri-stem'
                $resultados[$key].Riesgo = if ($diasSinUso -gt $thresholdDays) { 'ALTO' }
                                           elseif ($diasSinUso -gt 180)        { 'MEDIO' }
                                           else                                { 'BAJO'  }
            }
            $resultados[$key].Hits += [int]$row.Hits
        } else {
            $tipo   = if ($appPath -match 'api|svc|service') { 'API' } else { 'Web' }
            $riesgo = if ($diasSinUso -gt $thresholdDays) { 'ALTO' }
                      elseif ($diasSinUso -gt 180)        { 'MEDIO' }
                      else                                { 'BAJO'  }
            $resultados[$key] = [PSCustomObject]@{
                Sitio        = $site.Name
                Aplicacion   = $appPath
                Hits         = [int]$row.Hits
                UltimaUrl    = $row.'cs-uri-stem'
                UltimoAcceso = $fecha
                DiasSinUso   = $diasSinUso
                Tipo         = $tipo
                Riesgo       = $riesgo
            }
        }
    }
}

# Exportar ordenado por días sin uso (mayor primero)
$resultados.Values |
    Sort-Object DiasSinUso -Descending |
    Export-Csv $outputCsv -NoTypeInformation -Encoding UTF8

Write-Host "Reporte generado en: $outputCsv"

Resultado del script

El resultado del script es un archivo CSV con la siguiente estructura:

ColumnaDescripción
SitioNombre del sitio IIS (ej. Default Web Site)
AplicacionPath de la aplicación detectada (ej. /MiApp)
HitsTotal de peticiones exitosas acumuladas en todos los logs
UltimaUrlLa última URL exacta registrada para esa aplicación
UltimoAccesoFecha y hora del último acceso exitoso
DiasSinUsoDías transcurridos desde el último acceso hasta hoy
TipoWeb o API, detectado por el patrón en el path
RiesgoBAJO, MEDIO o ALTO según los días sin uso

Con este CSV ya puedo validar y descartar las aplicaciones que ya no son relevantes o tienen poco o nulo uso. El proceso es manual: abro el archivo en Excel, ordeno por DiasSinUso de mayor a menor y voy decidiendo cuáles se migran. Las de Riesgo ALTO son las candidatas a descartar, aunque siempre hay que validar con el negocio, ya que puede haber sistemas con uso estacional que solo se acceden unos días al año pero son críticos.

Posterior a esto, el siguiente paso es exportar la configuración de cada aplicativo seleccionado e importarla en el nuevo servidor, pero de esa parte les hablaré en mi próxima publicación.

Referencias

Mover o respaldar archivos en un NAS Synology con script personalizado

En esta ocasión les comparto una opcion personalizada para mover o respaldar archivos en un NAS Synology mediante un script definido por el usuario, ejecutado de forma recursiva mediante el programador de tareas.

Esto puede ser util para ejecutar cualquier tipo de script, no solo para mover o respaldar archivos.

Lo primero que realice fue montar una carpeta remota, para esto abrir File Station

Ingrese al menú Herramientas / Montar carpeta remota / Carpeta compartida de CIFS e incluí las configuraciones de la carpeta remota

Posteriormente se tendría que crear el script a ejecutar y almacenarlo en algún directorio dentro del NAS

Para el script utilice comandos muy básicos de Linux, los comandos cp y rm

El script quedo de la siguiente forma:

cd /volume1/directorioorigen/
cp -uvr /volume1/directorioorigen/ /volume1/directoriodestino/
rm -rv /volume1/directorioorigen/*

El comando cp sirve para copiar archivos y el rm sirve para eliminar archivos en el sistema operativo Linux, les incluyo una descripción de las opciones que maneje:

-u copia solo si el archivo es mas nuevo que el archivo destino o en el destino no existe
-v–verbose explain what is being done
-r–recursive copy directories recursively

Pienso en un futuro actualizar el script utilizando el comando rsync.

Por ultimo, para crear la tarea programada tienen que ingresar a Panel de Control / Programador de Tareas

Posteriormente, al menú Crear / Tareas Programadas / Script definido por el usuario

Ahí definirás el nombre de la tarea, periodicidad de ejecución y la ruta del script a ejecutar

También les recomiendo seguir las recomendaciones para guardar los resultados de la ejecución del script

Por el momento es todo, espero les sea de utilidad

Referencias:

Sugerencias para crear tareas y escribir scripts en el Programador de tareas

Montar carpetas remotas

Comandos linux que debes conocer

rsync: Delete Source File After Transfer