|
FOROS PROFESIONALES WINDEV, WEBDEV y WINDEV Mobile |
| | | | | |
| Inicio → WINDEV 25 → SUPER SCRIPT PARA VALIDAR W.A.S. E Ms Internet Information Server (Msiis) |
| SUPER SCRIPT PARA VALIDAR W.A.S. E Ms Internet Information Server (Msiis) |
| Iniciado por Boller, 22,sep. 2025 06:49 - 17 respuestas |
| |
| | | |
|
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 06:49 |
Usa POWERSHELL como administrador
########################################################### #SUPER SCRIPT PARA VALIDAR W.A.S. E Ms Internet Information Server (Msiis) Boa! Peguei os pontos dos tópicos do fórum que você enviou (checklist de usuário LOCAL e grupos, permissões nas pastas do IIS/PC SOFT, handlers .awp/.awws/.wb, correção do “Virtual Directory”, e a exigência do DISM funcionar) e empacotei num script PowerShell completo, idempotente e com “guard rails”. Ele instala e configura o IIS para o W.A.S., cria usuário local dedicado (opcional), aplica permissões, registra handlers/ISAPI do WDxxAWP.exe, cria app pool com identidade específica, cria aplicação virtual, testa a URL e roda um diagnóstico detalhado — com switches para habilitar passos “arriscados” apenas quando você quiser. Referências de onde veio cada exigência/prova de campo: checklist/diagnóstico e correção de “Virtual Directory” e handlers (.awp/.awws/.wb) ; necessidade do DISM operacional para o W.A.S. em alguns ambientes ; tópico base “WAS - Webdev Aplicativo Server” (coletânea) com boas práticas gerais de instalação e IIS “tudo verde” . ⸻ Script (salve como Setup-WAS-IIS.ps1 e execute em PowerShell administrador) #requires -RunAsAdministrator <# .SYNOPSIS Configuração COMPLETA do IIS + W.A.S. (WebDev Application Server) com diagnósticos. .DESCRIPTION - Instala recursos do IIS necessários ao WebDev/WAS - (Opcional) Cria usuário LOCAL dedicado e o coloca nos grupos Administrators e IIS_IUSRS - Configura Application Pool, ISAPI/Handlers (.awp/.awws/.wb), permissões NTFS - (Opcional) Faz correções para DISM funcionar quando exigido pelo WAS - Cria Aplicação Virtual e testa URL - Executa diagnóstico abrangente com resumo em % de prontidão Baseado em experiências reais e tópicos do fórum PC SOFT (checklist, erros de Virtual Directory, handlers e DISM). Autor: Adriano José Boller – PC SOFT Brasil | Versão 3.0 #> param( [Parameter(Mandatory=$false)] [ValidatePattern('^\d{4}$')] [string]$WebdevVersion = "2025", [Parameter(Mandatory=$false)] [string]$ProjectName = "", [Parameter(Mandatory=$false)] [string]$SiteName = "Default Web Site", [Parameter(Mandatory=$false)] [string]$LocalUser = "WebdevAdmin", [Parameter(Mandatory=$false)] [SecureString]$LocalUserPassword, [Parameter(Mandatory=$false)] [string]$InstallPathC = "C:\PC SOFT\WINDEV {0}" -f "2025", [Parameter(Mandatory=$false)] [string]$InstallPathD = "D:\PC SOFT\WINDEV {0}" -f "2025", # Opções de execução [switch]$CreateLocalUser, [switch]$ConfigureIIS, [switch]$ConfigureWAS, [switch]$CreateVirtualApp, [switch]$RunDiagnostics, # roda diagnóstico ao final [switch]$FixDism, # tenta corrigir o acesso ao DISM (situações específicas) [switch]$OpenFirewall, # abre portas 80/443 (e 21/990 se FTP for desejado) [switch]$CopyDllsToBin, # copia DLLs do engine para a pasta do AWP/BIN (NÃO para System32) [switch]$Force # força recriação de artefatos ) # ========================= # Inicialização e utilitários # ========================= $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $global:StartedAt = Get-Date $LogDir = "C:\Logs" if (!(Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $LogPath = Join-Path $LogDir ("WAS_IIS_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss")) function Write-Log([string]$msg, [ValidateSet("INFO","WARN","ERROR","OK","STEP")]$level="INFO"){ $stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "[{0}] [{1}] {2}" -f $stamp, $level, $msg Write-Host $line Add-Content -Path $LogPath -Value $line } function Confirm-Action([string]$question){ $ans = Read-Host "$question (S/N)" return ($ans -match '^(s|S|y|Y)$') } function Must-BeLocalAdmin { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $pri = New-Object Security.Principal.WindowsPrincipal($id) if (-not $pri.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Log "Execute como Administrador." "ERROR"; throw "Necessário administrador local." } } function Ensure-NotADUser { $u = [Security.Principal.WindowsIdentity]::GetCurrent().Name # Heurística: DOMAIN\user indica AD diferente do computador local (prática do fórum) if ($u.Contains('\') -and -not $u.StartsWith($env:COMPUTERNAME+'\')) { Write-Log "Usuário do AD detectado ($u). Use usuário LOCAL administrativo." "ERROR" throw "Não execute com usuário do AD." } } function Resolve-WebDevPaths { param([string]$ver) $c = $InstallPathC -replace '\{0\}', $ver $d = $InstallPathD -replace '\{0\}', $ver $c = $c -replace 'WINDEV', 'WEBDEV' # se máquina tem só WEBDEV instalado com pasta WEBDEV $d = $d -replace 'WINDEV', 'WEBDEV' $c2 = $InstallPathC -replace '\{0\}', $ver # Tenta ambas nomenclaturas (há ambientes com WINDEV contendo Engine do WAS) $candidates = @( (Join-Path $c "Programs\Engine\Win64x86\AWP"), (Join-Path $d "Programs\Engine\Win64x86\AWP"), (Join-Path $c2 "Programs\Engine\Win64x86\AWP"), (Join-Path $c "Programs\Engine\Win32\AWP"), (Join-Path $d "Programs\Engine\Win32\AWP") ) | Get-Unique foreach($p in $candidates){ if(Test-Path $p){ return $p } } return $null } function Get-AwpExe { param([string]$ver) # WD300awp.exe => WebDev 30, WD295awp.exe => 2025 beta, etc. Usaremos heurística: $awpFolder = Resolve-WebDevPaths -ver $ver if (!$awpFolder) { return $null } $files = Get-ChildItem -Path $awpFolder -Filter "WD*awp.exe" -ErrorAction SilentlyContinue | Sort-Object Name -Descending return $files | Select-Object -First 1 } # ========================= # Usuário local (opcional) # ========================= function Ensure-LocalWebdevUser { param([string]$User, [SecureString]$Pass, [switch]$force) $exists = Get-LocalUser -Name $User -ErrorAction SilentlyContinue if ($exists -and $force){ Write-Log "Removendo usuário existente $User" "WARN" Remove-LocalUser -Name $User -ErrorAction SilentlyContinue $exists = $null } if (!$exists){ if (-not $Pass){ $Pass = Read-Host -AsSecureString -Prompt "Senha para $User (mín. 8 chars)" } New-LocalUser -Name $User -Password $Pass -FullName "$User - WebDev Admin" -PasswordNeverExpires -UserMayNotChangePassword | Out-Null Write-Log "Usuário criado: $User" "OK" } else { Write-Log "Usuário já existe: $User" "INFO" } foreach($grp in @("Administrators","IIS_IUSRS")){ try { Add-LocalGroupMember -Group $grp -Member $User -ErrorAction SilentlyContinue } catch {} } Write-Log "Usuário '$User' está nos grupos Administrators e IIS_IUSRS" "OK" } # ========================= # IIS: instalação e base # ========================= function Ensure-IIS { Write-Log "Instalando recursos IIS..." "STEP" $features = @( "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures", "IIS-HttpErrors","IIS-HttpRedirect", "IIS-ApplicationDevelopment","IIS-NetFxExtensibility45","IIS-ASPNET45", "IIS-ISAPIExtensions","IIS-ISAPIFilter", "IIS-HealthAndDiagnostics","IIS-HttpLogging", "IIS-Security","IIS-RequestFiltering", "IIS-Performance", "IIS-WebServerManagementTools","IIS-ManagementConsole", "IIS-IIS6ManagementCompatibility","IIS-Metabase" ) foreach($f in $features){ try { Enable-WindowsOptionalFeature -Online -FeatureName $f -All -NoRestart -ErrorAction SilentlyContinue | Out-Null } catch {} } # Inicia/garante site padrão Import-Module WebAdministration -ErrorAction SilentlyContinue $site = Get-Website -Name $SiteName -ErrorAction SilentlyContinue if ($site -and $site.State -ne "Started"){ Start-Website -Name $SiteName } Write-Log "IIS instalado e $SiteName ativo" "OK" } function Configure-AppPool { param([string]$Pool="WebdevAppPool",[string]$User,[SecureString]$Pass) Import-Module WebAdministration -ErrorAction SilentlyContinue if (-not (Get-IISAppPool -Name $Pool -ErrorAction SilentlyContinue)){ New-IISAppPool -Name $Pool | Out-Null } if ($User -and $Pass){ $plain = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Pass)) Set-IISAppPool -Name $Pool -ProcessModel @{ identityType="SpecificUser"; userName=$User; password=$plain } } # Ajustes recomendados para apps longas Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/recycling/periodicRestart" -Value @{time="00:00:00"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{startMode="AlwaysRunning"} -PSPath IIS:\ -ErrorAction SilentlyContinue Write-Log "Application Pool '$Pool' configurado" "OK" # Atribui pool ao site if (Get-Website -Name $SiteName -ErrorAction SilentlyContinue){ Set-ItemProperty "IIS:\Sites\$SiteName" -Name applicationPool -Value $Pool Write-Log "Pool '$Pool' aplicado ao site '$SiteName'" "OK" } } # ========================= # Handlers / ISAPI do W.A.S. # ========================= function Configure-WebDevHandlers { param([string]$awpExePath) if (-not (Test-Path $awpExePath)){ throw "WDxxawp.exe não encontrado ($awpExePath)" } Import-Module WebAdministration -ErrorAction SilentlyContinue # Permite ISAPI para o executável do W.A.S. $isapi = Get-WebConfiguration "system.webServer/security/isapiCgiRestriction/add[@path='$awpExePath']" -ErrorAction SilentlyContinue if (-not $isapi){ Add-WebConfiguration "system.webServer/security/isapiCgiRestriction" -Value @{ path=$awpExePath; allowed=$true; description=("WebDev AWP " + $WebdevVersion) } -ErrorAction SilentlyContinue Write-Log "ISAPI permitido: $awpExePath" "OK" } # Limpa handlers antigos “WebDev-*” $handlers = Get-WebConfiguration "system.webServer/handlers/add" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue | Where-Object { $_.name -like "WebDev-*" -or $_.path -like "*.awp" -or $_.path -like "*.awws" -or $_.path -like "*.wb" } foreach($h in $handlers){ Remove-WebConfiguration "system.webServer/handlers/add[@name='$($h.name)']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue } $map = @( @{ Ext=".awp"; Name="WebDev-AWP" }, @{ Ext=".awws"; Name="WebDev-AWWS" }, @{ Ext=".wb"; Name="WebDev-WB" } ) foreach($m in $map){ Add-WebConfiguration "system.webServer/handlers" -PSPath "IIS:\Sites\$SiteName" -Value @{ name=$m.Name; path=("*{0}" -f $m.Ext); verb="*"; modules="IsapiModule"; scriptProcessor=$awpExePath; resourceType="File" } -ErrorAction SilentlyContinue } # MIME types (tolerante a duplicatas) $mimes = @( @{ext=".awp"; type="application/x-webdev-awp"}, @{ext=".awws"; type="application/x-webdev-awws"}, @{ext=".wb"; type="application/x-webdev-wb"} ) foreach($mm in $mimes){ try { Add-WebConfiguration "system.webServer/staticContent" -PSPath "IIS:\Sites\$SiteName" -Value @{ fileExtension=$mm.ext; mimeType=$mm.type } -ErrorAction Stop } catch { } } Write-Log "Handlers/MIME do WebDev configurados para $SiteName" "OK" } # ========================= # Permissões NTFS úteis # ========================= function Grant-Permissions { param([string]$Path,[string]$User) if(-not (Test-Path $Path)){ New-Item -ItemType Directory -Path $Path -Force | Out-Null } $acl = Get-Acl $Path $rules = @( New-Object System.Security.AccessControl.FileSystemAccessRule($User, "FullControl","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("IIS_IUSRS", "Modify","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("NETWORK SERVICE", "ReadAndExecute","ContainerInherit, ObjectInherit","None","Allow") ) foreach($r in $rules){ $acl.SetAccessRule($r) } Set-Acl -Path $Path -AclObject $acl Write-Log "Permissões aplicadas em '$Path'" "OK" } # ========================= # Aplicação Virtual # ========================= function Ensure-VirtualApp { param([string]$Name,[string]$User) Import-Module WebAdministration -ErrorAction SilentlyContinue $physical = Join-Path "C:\inetpub\wwwroot" $Name if (!(Test-Path $physical)){ New-Item -ItemType Directory -Path $physical -Force | Out-Null } Grant-Permissions -Path $physical -User $User # Remove app anterior se Force if ($Force -and (Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ Remove-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue } if (-not (Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ New-WebApplication -Site $SiteName -Name $Name -PhysicalPath $physical -ApplicationPool "WebdevAppPool" | Out-Null } # web.config básico $webCfg = @"
"@ $webCfg | Out-File -FilePath (Join-Path $physical "web.config") -Encoding UTF8 -Force # WEB.INI mínimo (útil para diagnósticos do Checklist) $webIni = @" [PROJECT] PROJECTNAME=$Name PROJECTPATH=$physical VERSION=$WebdevVersion TIMEOUT=7200 "@ $webIni | Out-File -FilePath (Join-Path $physical "WEB.INI") -Encoding UTF8 -Force # Página de teste $html = @" $Name ✅ $Name online Servidor: $env:COMPUTERNAME Diretório: $physical "@ $html | Out-File -FilePath (Join-Path $physical "test.html") -Encoding UTF8 -Force Write-Log "Aplicação virtual '$Name' pronta em $physical" "OK" return $physical } # ========================= # Correção p/ DISM (opcional) # ========================= function Ensure-DismWorks { # Alguns ambientes de WAS exigem que o DISM rode corretamente (vide fórum) $dism = "$env:WINDIR\System32\dism.exe" if (!(Test-Path $dism)){ Write-Log "DISM não encontrado: $dism" "WARN"; return } try { & $dism /online /Get-CurrentEdition | Out-Null if ($LASTEXITCODE -eq 0){ Write-Log "DISM operacional." "OK"; return } } catch {} Write-Log "DISM apresentou erro. Aplicando correções leves (ACL no dism.exe)." "WARN" try{ takeown /f $dism | Out-Null icacls $dism /grant "Administrators:F" /Q | Out-Null icacls $dism /grant "IIS_IUSRS:RX" /Q | Out-Null } catch { Write-Log "Falha ao ajustar ACL do DISM: $_" "WARN" } } # ========================= # Abrir firewall (opcional) # ========================= function Ensure-Firewall { New-NetFirewallRule -DisplayName "IIS HTTP 80" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80 -ErrorAction SilentlyContinue | Out-Null New-NetFirewallRule -DisplayName "IIS HTTPS 443" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443 -ErrorAction SilentlyContinue | Out-Null Write-Log "Firewall: portas 80/443 liberadas" "OK" } # ========================= # Copiar DLLs para BIN do AWP (seguro) # ========================= function Copy-EngineDllsToAwpBin { param([string]$awpFolder) # Em vez de System32, mantenha as DLLs ao lado do WDxxawp.exe (boa prática de isolamento) $engineRoot = Split-Path $awpFolder -Parent $src = $engineRoot $dst = $awpFolder $patterns = @("wd*.dll","hf*.dll","*sql*.dll","*oracle*.dll","*mysql*.dll") $copied = 0 foreach($pat in $patterns){ Get-ChildItem -Path $src -Filter $pat -ErrorAction SilentlyContinue | ForEach-Object { Copy-Item $_.FullName -Destination (Join-Path $dst $_.Name) -Force -ErrorAction SilentlyContinue $copied++ } } Write-Log ("DLLs copiadas para AWP/BIN: {0}" -f $copied) "OK" } # ========================= # Testes e Diagnósticos # ========================= function Test-Endpoints { param([string]$AppName) try { $r = Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing -TimeoutSec 10 Write-Log "IIS responde (HTTP 80): $($r.StatusCode)" "OK" } catch { Write-Log "IIS não respondeu em http://localhost" "ERROR" } if ($AppName){ try { $url = "http://localhost/{0}/test.html" -f $AppName $r2 = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10 Write-Log "Aplicação '$AppName' responde: $($r2.StatusCode) - $url" "OK" } catch { Write-Log "Aplicação '$AppName' NÃO respondeu." "ERROR" } } } function Invoke-WasIisDiagnostics { param([string]$AppName,[string]$awpExe) Import-Module WebAdministration -ErrorAction SilentlyContinue $checks = [ordered]@{ "Usuário local administrativo" = $true "Usuário NÃO é AD" = $true "IIS instalado e ativo" = $false "Pool WebdevAppPool existe" = $false "WDxxawp.exe localizado" = $false "Handlers .awp/.awws/.wb" = $false "ISAPI liberado p/ AWP" = $false "Porta 80 acessível" = $false "Aplicação virtual existe" = $false "Permissões wwwroot/app OK" = $false "DISM funcional (opcional)" = $false } # AD/local try { Ensure-NotADUser } catch { $checks["Usuário NÃO é AD"] = $false } try { $id = [Security.Principal.WindowsIdentity]::GetCurrent() $pri = New-Object Security.Principal.WindowsPrincipal($id) $checks["Usuário local administrativo"] = $pri.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } catch { $checks["Usuário local administrativo"] = $false } # IIS $site = Get-Website -Name $SiteName -ErrorAction SilentlyContinue if ($site -and $site.State -eq "Started"){ $checks["IIS instalado e ativo"] = $true } # Pool if (Get-IISAppPool -Name "WebdevAppPool" -ErrorAction SilentlyContinue){ $checks["Pool WebdevAppPool existe"] = $true } # AWP if ($awpExe -and (Test-Path $awpExe)){ $checks["WDxxawp.exe localizado"] = $true } # Handlers (.awp) $hAwp = Get-WebConfiguration "system.webServer/handlers/add[@path='*.awp']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue $hAwws = Get-WebConfiguration "system.webServer/handlers/add[@path='*.awws']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue $hWb = Get-WebConfiguration "system.webServer/handlers/add[@path='*.wb']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue if ($hAwp -and $hAwws -and $hWb){ $checks["Handlers .awp/.awws/.wb"] = $true } # ISAPI $isapi = Get-WebConfiguration "system.webServer/security/isapiCgiRestriction/add[@path='$awpExe']" -ErrorAction SilentlyContinue if ($isapi){ $checks["ISAPI liberado p/ AWP"] = $true } # Porta 80 try { $r = Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing -TimeoutSec 5; if($r.StatusCode){ $checks["Porta 80 acessível"] = $true } } catch {} # App if ($AppName){ $app = Get-WebApplication -Site $SiteName -Name $AppName -ErrorAction SilentlyContinue if ($app){ $checks["Aplicação virtual existe"] = $true if (Test-Path $app.PhysicalPath){ $checks["Permissões wwwroot/app OK"] = $true } } } # DISM try { & "$env:WINDIR\System32\dism.exe" /online /Get-CurrentEdition | Out-Null if ($LASTEXITCODE -eq 0){ $checks["DISM funcional (opcional)"] = $true } } catch {} Write-Host "" Write-Host "📊 DIAGNÓSTICO WAS/IIS" Write-Host "----------------------" $ok=0; $tot=$checks.Count foreach($k in $checks.Keys){ $val = $checks[$k] if ($val){ Write-Host ("[OK] {0}" -f $k) -ForegroundColor Green; $ok++ } else { Write-Host ("[NOK] {0}" -f $k) -ForegroundColor Red } } $pct = [math]::Round(($ok/$tot)*100,2) Write-Host ("Resumo: {0}/{1} = {2}%" -f $ok,$tot,$pct) -ForegroundColor Cyan } # ========================= # Fluxo principal # ========================= try{ Must-BeLocalAdmin Ensure-NotADUser # Ajusta versões de caminho $InstallPathC = $InstallPathC -replace '\{0\}', $WebdevVersion $InstallPathD = $InstallPathD -replace '\{0\}', $WebdevVersion Write-Log "Início da configuração WAS/IIS (v$WebdevVersion) — Site: $SiteName — Projeto: $ProjectName" "STEP" if ($CreateLocalUser){ Ensure-LocalWebdevUser -User $LocalUser -Pass $LocalUserPassword -force:$Force } if ($ConfigureIIS){ Ensure-IIS if ($CreateLocalUser){ Configure-AppPool -Pool "WebdevAppPool" -User $LocalUser -Pass $LocalUserPassword } else { Configure-AppPool -Pool "WebdevAppPool" } } $awp = $null if ($ConfigureWAS){ $awpFile = Get-AwpExe -ver $WebdevVersion if (-not $awpFile){ throw "Não foi possível localizar WDxxawp.exe (verifique instalação do WebDev/WAS)" } $awp = $awpFile.FullName Write-Log "AWP detectado: $awp" "OK" Configure-WebDevHandlers -awpExePath $awp if ($CopyDllsToBin){ Copy-EngineDllsToAwpBin -awpFolder $awpFile.DirectoryName } } if ($OpenFirewall){ Ensure-Firewall } if ($FixDism){ Ensure-DismWorks } if ($CreateVirtualApp -and $ProjectName){ $userForPerms = ($CreateLocalUser) ? $LocalUser : "$env:USERNAME" Ensure-VirtualApp -Name $ProjectName -User $userForPerms | Out-Null iisreset | Out-Null Test-Endpoints -AppName $ProjectName } else { Test-Endpoints } if ($RunDiagnostics){ if (-not $awp){ $awpFile = Get-AwpExe -ver $WebdevVersion; if($awpFile){ $awp=$awpFile.FullName } } Invoke-WasIisDiagnostics -AppName $ProjectName -awpExe $awp } Write-Log ("Concluído em {0}s" -f [int](New-TimeSpan -Start $global:StartedAt -End (Get-Date)).TotalSeconds) "OK" } catch{ Write-Log "Falhou: $_" "ERROR" throw }
⸻
O que este script faz (e por quê) • Usuário LOCAL e grupos: cria/usa um usuário local (Administrators + IIS_IUSRS) e evita usuários AD para instalar/configurar (padrão do checklist e dos relatos de campo).  • IIS “tudo verde”: instala recursos exigidos (ASP.NET 4.8, ISAPI, Console, Metabase/IIS6 compatibility) e garante o Default Web Site iniciado.  • Handlers/MIME do WebDev: registra .awp / .awws / .wb apontando para o WDxxawp.exe e libera o ISAPI correspondente — condição verificada nos diagnósticos.  • Aplicação Virtual + WEB.INI + test.html: ajuda a matar o erro “Application: configuration of the virtual directory” criando o diretório físico, permissões, WEB.INI mínimo e uma URL de teste com status.  • DISM opcional: adiciona correção leve de ACL no dism.exe quando você usar -FixDism (apenas se realmente precisar, conforme relatos). Não mexe em System32 globalmente, nem desabilita SFC/UAC por padrão, para manter segurança.  • DLLs: se precisar, -CopyDllsToBin copia DLLs do engine para a pasta do AWP (lado do executável), evitando poluir o System32 (isolamento e reversibilidade melhores).
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 06:55 |
Segue um pacotão V1.0 PowerShell com as duas variantes que você pediu, no mesmo script: 1. Modo “unsafe/system-takeover” (opcional via switch) • Toma posse do C:\Windows\System32 (takeown + icacls), • Desativa UAC (e, opcionalmente, o Defender), • Ajusta ACL do dism.exe. 2. Modo “hardening” (opcional via switch) • App Pool em NoManagedCode, IdleTimeout = 0, recycling suprimido, AlwaysRunning + AutoStart, • web.config endurecido (limites de request, requestFiltering, headers de segurança), • HTTPS binding (porta 443) com SNI; tenta integrar com Certify The Web se detectado; se não, cria self-signed automática e já faz o bind.
⚠️ Aviso forte: o modo “unsafe/system-takeover” reduz a segurança do sistema e deve ser usado somente em labs ou para destravar cenários muito específicos e temporários. Em produção, prefira SEMPRE o modo “hardening” sem mexer no System32/UAC.
⸻
Script: Setup-WAS-IIS-Variants.ps1
#requires -RunAsAdministrator <# .SYNOPSIS Variantes "unsafe/system-takeover" e "hardening" para IIS + WAS (WebDev Application Server). .DESCRIPTION - Unsafe (opt-in): toma posse do System32, desativa UAC, ajusta ACL de dism.exe. - Hardening (opt-in): app pool NoManagedCode, IdleTimeout 0, reciclagem suprimida, AlwaysRunning/AutoStart, web.config endurecido, binding HTTPS (Certify The Web se presente; caso contrário self-signed). .NOTES Use switches -Unsafe e/ou -Hardening conforme necessidade. Pode combinar com criação de app virtual. #>
param( [Parameter(Mandatory=$false)] [ValidatePattern('^\d{4}$')] [string]$WebdevVersion = "2025",
[Parameter(Mandatory=$false)] [string]$SiteName = "Default Web Site",
[Parameter(Mandatory=$false)] [string]$AppPoolName = "WebdevAppPool",
[Parameter(Mandatory=$false)] [string]$ProjectName = "", # se quiser criar app virtual
[Parameter(Mandatory=$false)] [string]$PhysicalPath = "", # se vazio => C:\inetpub\wwwroot\$ProjectName
[Parameter(Mandatory=$false)] [string]$LocalUser = "WebdevAdmin",
[Parameter(Mandatory=$false)] [SecureString]$LocalUserPassword, # se criar identidade específica
[switch]$CreateLocalUser, # cria usuário local e adiciona em Administrators + IIS_IUSRS [switch]$ConfigureIIS, # instala features do IIS e liga site [switch]$ConfigureWAS, # registra handlers .awp/.awws/.wb + ISAPI (WDxxawp.exe) [switch]$CreateVirtualApp, # cria aplicação virtual e pagina de teste [switch]$Hardening, # aplica endurecimento (pool + web.config + https) [switch]$Unsafe, # toma posse do System32, desativa UAC, etc. (NÃO recomendado) [switch]$DisableDefenderAlso, # só funciona junto com -Unsafe (opcional) [switch]$OpenFirewall, # abre portas 80/443 no firewall [switch]$Force, # força recriar app virtual/itens existentes
# HTTPS/Cert [Parameter(Mandatory=$false)] [string]$Hostname = "", # FQDN para SNI [Parameter(Mandatory=$false)] [string]$CertifyPath = "C:\Program Files\CertifyTheWeb\Certify.exe", # se existir, tenta usar [Parameter(Mandatory=$false)] [string]$CertifyManagedItemName = "", # nome do "managed certificate" do Certify para este site [Parameter(Mandatory=$false)] [string]$ContactEmail = "" # e-mail (self-signed metadata) )
$ErrorActionPreference = "Stop" $LogDir = "C:\Logs"; if (!(Test-Path $LogDir)) { New-Item $LogDir -ItemType Directory -Force | Out-Null } $LogPath = Join-Path $LogDir ("WAS_IIS_VARIANTS_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss"))
function Log($m, $lvl="INFO"){ $ts=Get-Date -Format "yyyy-MM-dd HH:mm:ss"; $l="[$ts] [$lvl] $m"; Write-Host $l; Add-Content -Path $LogPath -Value $l }
function Assert-AdminLocal { $id=[Security.Principal.WindowsIdentity]::GetCurrent() $p =New-Object Security.Principal.WindowsPrincipal($id) if(-not $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){ Log "Execute como Administrador." "ERROR"; throw "Necessário administrador local." } $u = $id.Name if ($u.Contains('\') -and -not $u.StartsWith($env:COMPUTERNAME+'\')){ Log "Usuário AD detectado ($u). Recomenda-se usuário LOCAL, especialmente para WAS." "WARN" } }
function Ensure-IIS { Log "Instalando recursos IIS..." "STEP" $features = @( "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures", "IIS-HttpErrors","IIS-HttpRedirect", "IIS-ApplicationDevelopment","IIS-NetFxExtensibility45","IIS-ASPNET45", "IIS-ISAPIExtensions","IIS-ISAPIFilter", "IIS-HealthAndDiagnostics","IIS-HttpLogging", "IIS-Security","IIS-RequestFiltering", "IIS-Performance", "IIS-WebServerManagementTools","IIS-ManagementConsole", "IIS-IIS6ManagementCompatibility","IIS-Metabase" ) foreach($f in $features){ try{ Enable-WindowsOptionalFeature -Online -FeatureName $f -All -NoRestart -ErrorAction SilentlyContinue | Out-Null }catch{} } Import-Module WebAdministration -ErrorAction SilentlyContinue $site=Get-Website -Name $SiteName -ErrorAction SilentlyContinue if ($site -and $site.State -ne "Started"){ Start-Website -Name $SiteName } Log "IIS e '$SiteName' ok." "OK" }
function Ensure-LocalUser { param([string]$User,[SecureString]$Pass,[switch]$force) $ex = Get-LocalUser -Name $User -ErrorAction SilentlyContinue if($ex -and $force){ Log "Removendo $User" "WARN"; Remove-LocalUser -Name $User -ErrorAction SilentlyContinue; $ex=$null } if(!$ex){ if(!$Pass){ $Pass = Read-Host -AsSecureString -Prompt "Senha para $User (mín 8 chars)" } New-LocalUser -Name $User -Password $Pass -FullName "$User - WebDev Admin" -PasswordNeverExpires -UserMayNotChangePassword | Out-Null Log "Usuário criado: $User" "OK" } foreach($g in @("Administrators","IIS_IUSRS")){ try{ Add-LocalGroupMember -Group $g -Member $User -ErrorAction SilentlyContinue }catch{} } Log "Usuário '$User' em Administrators e IIS_IUSRS" "OK" }
function Configure-AppPoolBase { param([string]$Pool,[string]$User,[SecureString]$Pass) Import-Module WebAdministration -ErrorAction SilentlyContinue if(-not (Get-IISAppPool -Name $Pool -ErrorAction SilentlyContinue)){ New-IISAppPool -Name $Pool | Out-Null } if($User -and $Pass){ $plain=[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Pass)) Set-IISAppPool -Name $Pool -ProcessModel @{identityType="SpecificUser"; userName=$User; password=$plain} } # defaults razoáveis Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/recycling/periodicRestart" -Value @{time="00:00:00"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{startMode="AlwaysRunning"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-ItemProperty "IIS:\Sites\$SiteName" -Name applicationPool -Value $Pool Log "Pool '$Pool' base configurado no site '$SiteName'." "OK" }
function Configure-AppPoolHardening { param([string]$Pool) Import-Module WebAdministration -ErrorAction SilentlyContinue # NoManagedCode, IdleTimeout=0, sem reciclagem, AlwaysRunning Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{managedRuntimeVersion=""; } -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/processModel" -Value @{idleTimeout="00:00:00"; pingingEnabled="true"; pingInterval="00:00:30"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/recycling/periodicRestart" -Value @{time="00:00:00"; privateMemory="0"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{startMode="AlwaysRunning"} -PSPath IIS:\ -ErrorAction SilentlyContinue Log "Pool '$Pool' endurecido (NoManagedCode, IdleTimeout 0, Recycling off, AlwaysRunning)." "OK" }
function Get-AwpExe { param([string]$ver) # Heurística: procurar WD*awp.exe nas árvores comuns $roots = @( "C:\PC SOFT\WEBDEV $ver\Programs\Engine\Win64x86\AWP", "D:\PC SOFT\WEBDEV $ver\Programs\Engine\Win64x86\AWP", "C:\PC SOFT\WINDEV $ver\Programs\Engine\Win64x86\AWP", "D:\PC SOFT\WINDEV $ver\Programs\Engine\Win64x86\AWP", "C:\PC SOFT\WEBDEV $ver\Programs\Engine\Win32\AWP", "D:\PC SOFT\WEBDEV $ver\Programs\Engine\Win32\AWP" ) foreach($r in $roots){ if(Test-Path $r){ $f = Get-ChildItem $r -Filter "WD*awp.exe" -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1; if($f){return $f} } } return $null }
function Configure-WebDevHandlers { param([string]$awpExePath) if(!(Test-Path $awpExePath)){ throw "WDxxawp.exe não encontrado ($awpExePath)" } Import-Module WebAdministration -ErrorAction SilentlyContinue # Permite ISAPI $isapi = Get-WebConfiguration "system.webServer/security/isapiCgiRestriction/add[@path='$awpExePath']" -ErrorAction SilentlyContinue if(!$isapi){ Add-WebConfiguration "system.webServer/security/isapiCgiRestriction" -Value @{path=$awpExePath; allowed=$true; description=("WebDev AWP "+$WebdevVersion)} -ErrorAction SilentlyContinue } # Limpa handlers antigos $hs = Get-WebConfiguration "system.webServer/handlers/add" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue | Where-Object { $_.path -like "*.awp" -or $_.path -like "*.awws" -or $_.path -like "*.wb" } foreach($h in $hs){ Remove-WebConfiguration "system.webServer/handlers/add[@name='$($h.name)']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue } # Registra handlers foreach($m in @(@{Ext=".awp";Name="WebDev-AWP"}, @{Ext=".awws";Name="WebDev-AWWS"}, @{Ext=".wb";Name="WebDev-WB"})){ Add-WebConfiguration "system.webServer/handlers" -PSPath "IIS:\Sites\$SiteName" -Value @{ name=$m.Name; path=("*{0}" -f $m.Ext); verb="*"; modules="IsapiModule"; scriptProcessor=$awpExePath; resourceType="File" } -ErrorAction SilentlyContinue } # MIME types (tolerante) foreach($mm in @(@{ext=".awp";type="application/x-webdev-awp"}, @{ext=".awws";type="application/x-webdev-awws"}, @{ext=".wb";type="application/x-webdev-wb"})){ try{ Add-WebConfiguration "system.webServer/staticContent" -PSPath "IIS:\Sites\$SiteName" -Value @{fileExtension=$mm.ext; mimeType=$mm.type} -ErrorAction Stop } catch {} } Log "Handlers/MIME do WebDev configurados." "OK" }
function Grant-Permissions($Path,$User){ if(!(Test-Path $Path)){ New-Item -ItemType Directory -Path $Path -Force | Out-Null } $acl=Get-Acl $Path $rules=@( New-Object System.Security.AccessControl.FileSystemAccessRule($User,"FullControl","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("IIS_IUSRS","Modify","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("NETWORK SERVICE","ReadAndExecute","ContainerInherit, ObjectInherit","None","Allow") ) foreach($r in $rules){ $acl.SetAccessRule($r) } Set-Acl -Path $Path -AclObject $acl Log "Permissões em '$Path' aplicadas." "OK" }
function Ensure-VirtualApp { param([string]$Name,[string]$Pool,[string]$User) Import-Module WebAdministration -ErrorAction SilentlyContinue $phys = ($PhysicalPath) ? $PhysicalPath : (Join-Path "C:\inetpub\wwwroot" $Name) if ($Force -and (Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ Remove-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue } if(!(Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ New-WebApplication -Site $SiteName -Name $Name -PhysicalPath $phys -ApplicationPool $Pool | Out-Null } Grant-Permissions -Path $phys -User $User
# web.config (será sobrescrito no hardening) $web = @" <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <compilation targetFramework="4.8" debug="false"/> <httpRuntime targetFramework="4.8" executionTimeout="7200" maxRequestLength="102400"/> <sessionState timeout="120"/> <trust level="Full"/> </system.web> <system.webServer> <defaultDocument> <files> <clear/> <add value="index.awp"/> <add value="default.awp"/> <add value="login.awp"/> </files> </defaultDocument> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration> "@ if(!(Test-Path $phys)){ New-Item -ItemType Directory -Path $phys -Force | Out-Null } $web | Out-File -FilePath (Join-Path $phys "web.config") -Encoding UTF8 -Force
# página de teste "<h1>✅ $Name online</h1>" | Out-File -FilePath (Join-Path $phys "test.html") -Encoding UTF8 -Force Log "Aplicação virtual '$Name' pronta em $phys" "OK" return $phys }
# ---------- Hardening: web.config endurecido ---------- function Write-HardenedWebConfig { param([string]$PathToApp) $cfg = @" <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <compilation debug="false" targetFramework="4.8" /> <httpRuntime targetFramework="4.8" executionTimeout="3600" maxRequestLength="51200" /> <sessionState timeout="60" /> <customErrors mode="RemoteOnly" /> </system.web>
<system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="52428800" maxUrl="2048" maxQueryString="1024" /> <fileExtensions> <add fileExtension=".config" allowed="false" /> <add fileExtension=".ini" allowed="false" /> </fileExtensions> <denyUrlSequences> <add sequence=".."/> <add sequence="./"/> <add sequence="%"/> </denyUrlSequences> <allowDoubleEscaping="false" /> </requestFiltering> </security>
<httpProtocol> <customHeaders> <add name="X-Content-Type-Options" value="nosniff" /> <add name="X-Frame-Options" value="SAMEORIGIN" /> <add name="X-XSS-Protection" value="1; mode=block" /> <add name="Referrer-Policy" value="no-referrer-when-downgrade" /> <add name="Content-Security-Policy" value="default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:" /> <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" /> </customHeaders> </httpProtocol>
<validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true" /> <directoryBrowse enabled="false" /> </system.webServer> </configuration> "@ $cfg | Out-File -FilePath (Join-Path $PathToApp "web.config") -Encoding UTF8 -Force Log "web.config endurecido aplicado em '$PathToApp'." "OK" }
# ---------- HTTPS binding ---------- function Ensure-HttpsBinding { param([string]$Host,[string]$Site) Import-Module WebAdministration -ErrorAction SilentlyContinue
if([string]::IsNullOrWhiteSpace($Host)){ Log "Hostname não informado (-Hostname). Criando self-signed sem SNI em 0.0.0.0:443." "WARN" }
# 1) Tenta Certify The Web (se presente e com ManagedItemName) if((Test-Path $CertifyPath) -and -not [string]::IsNullOrWhiteSpace($CertifyManagedItemName)){ try{ Log "Detectado Certify The Web. Tentando renovar/aplicar certificado '$CertifyManagedItemName'..." "STEP" & "$CertifyPath" renew --manageditem "$CertifyManagedItemName" | Out-Null Start-Sleep 3 Log "Solicitado 'renew' ao Certify." "OK" } catch { Log "Falha ao invocar Certify: $_" "WARN" } }
# 2) Caso não haja cert pronto, cria self-signed e aplica SNI (se Host fornecido) $cert = $null try { if(-not [string]::IsNullOrWhiteSpace($Host)){ $cert = New-SelfSignedCertificate -DnsName $Host -CertStoreLocation "cert:\LocalMachine\My" -FriendlyName "SelfSigned-$Host" -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(1) } else { $cert = New-SelfSignedCertificate -DnsName "localhost" -CertStoreLocation "cert:\LocalMachine\My" -FriendlyName "SelfSigned-localhost" -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(1) } Log "Certificado self-signed criado: $($cert.Thumbprint)" "OK" } catch { Log "Falha ao criar self-signed: $_" "ERROR"; throw }
# Remove binding 443 antigo do site (se Force) if($Force){ try{ Get-WebBinding -Name $Site -Protocol https -ErrorAction SilentlyContinue | Remove-WebBinding -ErrorAction SilentlyContinue }catch{} }
# Cria binding https com SNI se Host presente if(-not [string]::IsNullOrWhiteSpace($Host)){ New-WebBinding -Name $Site -Protocol https -Port 443 -HostHeader $Host -SslFlags 1 | Out-Null # SNI # Atribui o certificado ao binding SNI $certPath = "IIS:\SslBindings\0.0.0.0!443!$Host" if(Test-Path $certPath){ Remove-Item $certPath -Force -ErrorAction SilentlyContinue } New-Item -Path "IIS:\SslBindings\0.0.0.0!443!$Host" -Thumbprint $cert.Thumbprint -SSLFlags 1 | Out-Null Log "HTTPS com SNI em $Host:443 aplicado ao site '$Site'." "OK" } else { # Sem host header (wildcard) New-WebBinding -Name $Site -Protocol https -Port 443 -SslFlags 0 | Out-Null $certPath = "IIS:\SslBindings\0.0.0.0!443" if(Test-Path $certPath){ Remove-Item $certPath -Force -ErrorAction SilentlyContinue } New-Item -Path "IIS:\SslBindings\0.0.0.0!443" -Thumbprint $cert.Thumbprint -SSLFlags 0 | Out-Null Log "HTTPS 0.0.0.0:443 aplicado ao site '$Site'." "OK" } }
# ---------- Unsafe/System Takeover ---------- function Do-UnsafeSystemTakeover { Log "ATENÇÃO: executando modo UNSAFE (System32 ownership + UAC off)." "WARN" # 1) Desativar UAC (requer reboot p/ efeito completo) try{ reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA /t REG_DWORD /d 0 /f | Out-Null reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v ConsentPromptBehaviorAdmin /t REG_DWORD /d 0 /f | Out-Null Log "UAC desativado via registro (requer reinicialização)." "OK" } catch { Log "Falha ao desativar UAC: $_" "ERROR" }
# 2) Tomar posse do System32 e conceder permissões $sys32 = "$env:WINDIR\System32" try{ takeown /f $sys32 /r /d Y | Out-Null icacls $sys32 /grant "Administrators:(OI)(CI)F" /T /Q | Out-Null Log "System32: ownership e FullControl para Administrators." "OK" } catch { Log "Falha ao tomar posse/ACL System32: $_" "ERROR" }
# 3) Ajustar ACL do dism.exe (casos de WAS que checam DISM) $dism = Join-Path $sys32 "dism.exe" if(Test-Path $dism){ try{ takeown /f $dism | Out-Null icacls $dism /grant "Administrators:F" /Q | Out-Null icacls $dism /grant "IIS_IUSRS:RX" /Q | Out-Null Log "ACL de dism.exe ajustada." "OK" } catch { Log "Falha ao ajustar ACL do dism.exe: $_" "WARN" } }
# 4) (Opcional) Desativar Defender (não recomendado) if($DisableDefenderAlso){ try{ reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender" /v DisableAntiSpyware /t REG_DWORD /d 1 /f | Out-Null reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" /v DisableRealtimeMonitoring /t REG_DWORD /d 1 /f | Out-Null Log "Windows Defender desativado (política)." "WARN" } catch { Log "Falha ao desativar Defender: $_" "WARN" } }
Log "Modo UNSAFE concluído. ⚠️ Recomenda-se REINICIAR o servidor." "WARN" }
# ---------- Firewall ---------- function Ensure-Firewall { try{ New-NetFirewallRule -DisplayName "IIS HTTP 80" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80 -ErrorAction SilentlyContinue | Out-Null New-NetFirewallRule -DisplayName "IIS HTTPS 443" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443 -ErrorAction SilentlyContinue | Out-Null Log "Firewall liberado para 80/443." "OK" } catch { Log "Falha ao abrir firewall: $_" "WARN" } }
# ======================= # EXECUÇÃO # ======================= try{ Assert-AdminLocal
if($CreateLocalUser){ Ensure-LocalUser -User $LocalUser -Pass $LocalUserPassword -force:$Force }
if($ConfigureIIS){ Ensure-IIS }
# App Pool base (identidade opcional) if($ConfigureIIS){ if($CreateLocalUser){ Configure-AppPoolBase -Pool $AppPoolName -User $LocalUser -Pass $LocalUserPassword } else{ Configure-AppPoolBase -Pool $AppPoolName } }
# WAS handlers if($ConfigureWAS){ $awp = Get-AwpExe -ver $WebdevVersion if(!$awp){ throw "WD*awp.exe não encontrado. Verifique instalação do WebDev/WAS ($WebdevVersion)." } Configure-WebDevHandlers -awpExePath $awp.FullName }
# App virtual if($CreateVirtualApp -and $ProjectName){ $userForPerms = ($CreateLocalUser) ? $LocalUser : "$env:USERNAME" $appPath = Ensure-VirtualApp -Name $ProjectName -Pool $AppPoolName -User $userForPerms }
# Hardening if($Hardening){ Configure-AppPoolHardening -Pool $AppPoolName if($ProjectName){ if(-not $appPath){ $appPath = (Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue).PhysicalPath if([string]::IsNullOrWhiteSpace($appPath)){ $appPath = Join-Path "C:\inetpub\wwwroot" $ProjectName } } Write-HardenedWebConfig -PathToApp $appPath } # HTTPS Ensure-HttpsBinding -Host $Hostname -Site $SiteName }
# Unsafe if($Unsafe){ Do-UnsafeSystemTakeover }
if($OpenFirewall){ Ensure-Firewall }
# Testes básicos try{ $r = Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing -TimeoutSec 10 Log "IIS responde (HTTP): $($r.StatusCode)" "OK" } catch { Log "IIS não respondeu em http://localhost" "WARN" }
if($ProjectName){ try{ $url = "http://localhost/{0}/test.html" -f $ProjectName $r2 = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10 Log "App '$ProjectName' responde ($($r2.StatusCode)): $url" "OK" } catch { Log "App '$ProjectName' não respondeu." "WARN" } }
Log "Concluído." "OK" } catch{ Log "Falha: $_" "ERROR" throw }
⸻
Exemplos de uso
1) Hardening completo (pool endurecido + web.config + HTTPS com SNI e self-signed fallback)
.\Setup-WAS-IIS-Variants.ps1 ` -WebdevVersion 2025 ` -ConfigureIIS -ConfigureWAS ` -CreateLocalUser -LocalUser WebdevAdmin ` -CreateVirtualApp -ProjectName "MinhaApp" ` -Hardening -Hostname "app.seudominio.com.br" ` -OpenFirewall
Se você já usa Certify The Web e tem um Managed Item criado para esse host, acrescente:
-CertifyManagedItemName "app.seudominio.com.br" ` -CertifyPath "C:\Program Files\CertifyTheWeb\Certify.exe"
2) Unsafe/system-takeover (apenas quando for inevitável)
.\Setup-WAS-IIS-Variants.ps1 -Unsafe -DisableDefenderAlso
⚠️ Reinicie o servidor depois.
3) Hardening em site já existente (sem criar app virtual)
.\Setup-WAS-IIS-Variants.ps1 ` -SiteName "Default Web Site" ` -AppPoolName "WebdevAppPool" ` -Hardening -Hostname "portal.seudominio.com.br"
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 06:58 |
Perfeito — aqui vai a v2 com tudo que você pediu adicionado, já no mesmo script (hardening + crypto + HTTP/2 + app init + COOP/COEP + tentativa de integração Certify The Web com fallback self-signed).
⚠️ Avisos rápidos • -Unsafe continua existindo e não é recomendado em produção. • Políticas de TLS/HTTP/2 via registro exigem reinicialização do serviço HTTP (ou do servidor) para efeito pleno. • A CLI do Certify The Web varia por versão; incluí detecção e tentativas seguras (renew, reapply). Se não houver managed item, uso self-signed com SNI.
⸻
Script — Setup-WAS-IIS-Variants-v2.ps1
#requires -RunAsAdministrator <# v2: Hardening + TLS 1.2/1.3, HTTP/2, appInitialization, headers COOP/COEP, Certify The Web (CLI) com fallback. #>
param( [ValidatePattern('^\d{4}$')] [string]$WebdevVersion = "2025", [string]$SiteName = "Default Web Site", [string]$AppPoolName = "WebdevAppPool", [string]$ProjectName = "", [string]$PhysicalPath = "", [string]$LocalUser = "WebdevAdmin", [SecureString]$LocalUserPassword,
[switch]$CreateLocalUser, [switch]$ConfigureIIS, [switch]$ConfigureWAS, [switch]$CreateVirtualApp, [switch]$Hardening, [switch]$Unsafe, [switch]$DisableDefenderAlso, [switch]$OpenFirewall, [switch]$Force,
# HTTPS / Certify [string]$Hostname = "", [string]$CertifyPath = "C:\Program Files\CertifyTheWeb\Certify.exe", [string]$CertifyManagedItemName = "", # Se existir, tentamos renew/reapply [string]$ContactEmail = "" # metadados self-signed )
$ErrorActionPreference = "Stop" $LogDir = "C:\Logs"; if (!(Test-Path $LogDir)) { New-Item $LogDir -ItemType Directory -Force | Out-Null } $LogPath = Join-Path $LogDir ("WAS_IIS_VARIANTS_V2_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss")) function Log($m, $lvl="INFO"){ $ts=Get-Date -Format "yyyy-MM-dd HH:mm:ss"; $l="[$ts] [$lvl] $m"; Write-Host $l; Add-Content -Path $LogPath -Value $l }
function Assert-AdminLocal { $id=[Security.Principal.WindowsIdentity]::GetCurrent() $p =New-Object Security.Principal.WindowsPrincipal($id) if(-not $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){ Log "Execute como Administrador." "ERROR"; throw "Necessário administrador local." } $u = $id.Name if ($u.Contains('\') -and -not $u.StartsWith($env:COMPUTERNAME+'\')){ Log "Usuário AD detectado ($u). Recomenda-se usuário LOCAL, especialmente para WAS." "WARN" } }
function Ensure-IIS { Log "Instalando recursos IIS..." "STEP" $features = @( "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures", "IIS-HttpErrors","IIS-HttpRedirect", "IIS-ApplicationDevelopment","IIS-NetFxExtensibility45","IIS-ASPNET45", "IIS-ISAPIExtensions","IIS-ISAPIFilter", "IIS-HealthAndDiagnostics","IIS-HttpLogging", "IIS-Security","IIS-RequestFiltering", "IIS-Performance", "IIS-WebServerManagementTools","IIS-ManagementConsole", "IIS-IIS6ManagementCompatibility","IIS-Metabase" ) foreach($f in $features){ try{ Enable-WindowsOptionalFeature -Online -FeatureName $f -All -NoRestart -ErrorAction SilentlyContinue | Out-Null }catch{} } Import-Module WebAdministration -ErrorAction SilentlyContinue $site=Get-Website -Name $SiteName -ErrorAction SilentlyContinue if ($site -and $site.State -ne "Started"){ Start-Website -Name $SiteName } Log "IIS e '$SiteName' ok." "OK" }
function Ensure-LocalUser { param([string]$User,[SecureString]$Pass,[switch]$force) $ex = Get-LocalUser -Name $User -ErrorAction SilentlyContinue if($ex -and $force){ Log "Removendo $User" "WARN"; Remove-LocalUser -Name $User -ErrorAction SilentlyContinue; $ex=$null } if(!$ex){ if(!$Pass){ $Pass = Read-Host -AsSecureString -Prompt "Senha para $User (mín 8 chars)" } New-LocalUser -Name $User -Password $Pass -FullName "$User - WebDev Admin" -PasswordNeverExpires -UserMayNotChangePassword | Out-Null Log "Usuário criado: $User" "OK" } foreach($g in @("Administrators","IIS_IUSRS")){ try{ Add-LocalGroupMember -Group $g -Member $User -ErrorAction SilentlyContinue }catch{} } Log "Usuário '$User' em Administrators e IIS_IUSRS" "OK" }
function Configure-AppPoolBase { param([string]$Pool,[string]$User,[SecureString]$Pass) Import-Module WebAdministration -ErrorAction SilentlyContinue if(-not (Get-IISAppPool -Name $Pool -ErrorAction SilentlyContinue)){ New-IISAppPool -Name $Pool | Out-Null } if($User -and $Pass){ $plain=[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Pass)) Set-IISAppPool -Name $Pool -ProcessModel @{identityType="SpecificUser"; userName=$User; password=$plain} } Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/recycling/periodicRestart" -Value @{time="00:00:00"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{startMode="AlwaysRunning"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-ItemProperty "IIS:\Sites\$SiteName" -Name applicationPool -Value $Pool Log "Pool '$Pool' base configurado no site '$SiteName'." "OK" }
function Configure-AppPoolHardening { param([string]$Pool) Import-Module WebAdministration -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{managedRuntimeVersion=""; } -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/processModel" -Value @{idleTimeout="00:00:00"; pingingEnabled="true"; pingInterval="00:00:30"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/recycling/periodicRestart" -Value @{time="00:00:00"; privateMemory="0"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{startMode="AlwaysRunning"} -PSPath IIS:\ -ErrorAction SilentlyContinue # AutoStart no nível do site Set-WebConfiguration "/system.applicationHost/sites/site[@name='$SiteName']" -Value @{ serverAutoStart="true" } -PSPath IIS:\ -ErrorAction SilentlyContinue Log "Pool '$Pool' endurecido (NoManagedCode, IdleTimeout 0, Recycling off, AlwaysRunning) + AutoStart." "OK" }
function Get-AwpExe { param([string]$ver) $roots = @( "C:\PC SOFT\WEBDEV $ver\Programs\Engine\Win64x86\AWP", "D:\PC SOFT\WEBDEV $ver\Programs\Engine\Win64x86\AWP", "C:\PC SOFT\WINDEV $ver\Programs\Engine\Win64x86\AWP", "D:\PC SOFT\WINDEV $ver\Programs\Engine\Win64x86\AWP", "C:\PC SOFT\WEBDEV $ver\Programs\Engine\Win32\AWP", "D:\PC SOFT\WEBDEV $ver\Programs\Engine\Win32\AWP" ) foreach($r in $roots){ if(Test-Path $r){ $f = Get-ChildItem $r -Filter "WD*awp.exe" -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1; if($f){return $f} } } return $null }
function Configure-WebDevHandlers { param([string]$awpExePath) if(!(Test-Path $awpExePath)){ throw "WDxxawp.exe não encontrado ($awpExePath)" } Import-Module WebAdministration -ErrorAction SilentlyContinue # ISAPI $isapi = Get-WebConfiguration "system.webServer/security/isapiCgiRestriction/add[@path='$awpExePath']" -ErrorAction SilentlyContinue if(!$isapi){ Add-WebConfiguration "system.webServer/security/isapiCgiRestriction" -Value @{path=$awpExePath; allowed=$true; description=("WebDev AWP "+$WebdevVersion)} -ErrorAction SilentlyContinue } # Handlers $hs = Get-WebConfiguration "system.webServer/handlers/add" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue | Where-Object { $_.path -like "*.awp" -or $_.path -like "*.awws" -or $_.path -like "*.wb" } foreach($h in $hs){ Remove-WebConfiguration "system.webServer/handlers/add[@name='$($h.name)']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue } foreach($m in @(@{Ext=".awp";Name="WebDev-AWP"}, @{Ext=".awws";Name="WebDev-AWWS"}, @{Ext=".wb";Name="WebDev-WB"})){ Add-WebConfiguration "system.webServer/handlers" -PSPath "IIS:\Sites\$SiteName" -Value @{ name=$m.Name; path=("*{0}" -f $m.Ext); verb="*"; modules="IsapiModule"; scriptProcessor=$awpExePath; resourceType="File" } -ErrorAction SilentlyContinue } # MIME (tolerante) foreach($mm in @(@{ext=".awp";type="application/x-webdev-awp"}, @{ext=".awws";type="application/x-webdev-awws"}, @{ext=".wb";type="application/x-webdev-wb"})){ try{ Add-WebConfiguration "system.webServer/staticContent" -PSPath "IIS:\Sites\$SiteName" -Value @{fileExtension=$mm.ext; mimeType=$mm.type} -ErrorAction Stop } catch {} } Log "Handlers/MIME do WebDev configurados." "OK" }
function Grant-Permissions($Path,$User){ if(!(Test-Path $Path)){ New-Item -ItemType Directory -Path $Path -Force | Out-Null } $acl=Get-Acl $Path $rules=@( New-Object System.Security.AccessControl.FileSystemAccessRule($User,"FullControl","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("IIS_IUSRS","Modify","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("NETWORK SERVICE","ReadAndExecute","ContainerInherit, ObjectInherit","None","Allow") ) foreach($r in $rules){ $acl.SetAccessRule($r) } Set-Acl -Path $Path -AclObject $acl Log "Permissões em '$Path' aplicadas." "OK" }
function Ensure-VirtualApp { param([string]$Name,[string]$Pool,[string]$User) Import-Module WebAdministration -ErrorAction SilentlyContinue $phys = ($PhysicalPath) ? $PhysicalPath : (Join-Path "C:\inetpub\wwwroot" $Name) if ($Force -and (Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ Remove-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue } if(!(Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ New-WebApplication -Site $SiteName -Name $Name -PhysicalPath $phys -ApplicationPool $Pool | Out-Null } Grant-Permissions -Path $phys -User $User
# web.config básico (será sobrescrito no hardening) $web = @" <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <compilation targetFramework="4.8" debug="false"/> <httpRuntime targetFramework="4.8" executionTimeout="7200" maxRequestLength="102400"/> <sessionState timeout="120"/> <trust level="Full"/> </system.web> <system.webServer> <defaultDocument> <files> <clear/> <add value="index.awp"/> <add value="default.awp"/> <add value="login.awp"/> </files> </defaultDocument> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration> "@ if(!(Test-Path $phys)){ New-Item -ItemType Directory -Path $phys -Force | Out-Null } $web | Out-File -FilePath (Join-Path $phys "web.config") -Encoding UTF8 -Force "<h1>✅ $Name online</h1>" | Out-File -FilePath (Join-Path $phys "test.html") -Encoding UTF8 -Force Log "Aplicação virtual '$Name' pronta em $phys" "OK" return $phys }
# ---------- v2: web.config HARDENED + COOP/COEP + CSP ---------- function Write-HardenedWebConfig { param([string]$PathToApp) $cfg = @" <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.web> <compilation debug="false" targetFramework="4.8" /> <httpRuntime targetFramework="4.8" executionTimeout="3600" maxRequestLength="51200" /> <sessionState timeout="60" /> <customErrors mode="RemoteOnly" /> </system.web>
<system.webServer> <security> <requestFiltering> <requestLimits maxAllowedContentLength="52428800" maxUrl="2048" maxQueryString="1024" /> <fileExtensions> <add fileExtension=".config" allowed="false" /> <add fileExtension=".ini" allowed="false" /> </fileExtensions> <denyUrlSequences> <add sequence=".."/> <add sequence="./"/> <add sequence="%"/> </denyUrlSequences> <allowDoubleEscaping="false" /> </requestFiltering> </security>
<httpProtocol> <customHeaders> <add name="X-Content-Type-Options" value="nosniff" /> <add name="X-Frame-Options" value="SAMEORIGIN" /> <add name="X-XSS-Protection" value="1; mode=block" /> <add name="Referrer-Policy" value="no-referrer-when-downgrade" /> <add name="Cross-Origin-Opener-Policy" value="same-origin"/> <add name="Cross-Origin-Embedder-Policy" value="require-corp"/> <add name="Content-Security-Policy" value="default-src 'self' data: blob: 'unsafe-inline' 'unsafe-eval'"/> <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" /> </customHeaders> </httpProtocol>
<validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true" /> <directoryBrowse enabled="false" />
<!-- v2: appInitialization (pré-aquecimento) --> <applicationInitialization doAppInitAfterRestart="true"> <add initializationPage="/index.awp" /> <add initializationPage="/default.awp" /> </applicationInitialization> </system.webServer> </configuration> "@ $cfg | Out-File -FilePath (Join-Path $PathToApp "web.config") -Encoding UTF8 -Force Log "web.config endurecido (v2) aplicado em '$PathToApp'." "OK" }
# ---------- v2: Habilitar HTTP/2 e TLS 1.2/1.3; desabilitar legados ---------- function Set-HTTP2-And-TLS-Policy { Log "Aplicando política de HTTP/2 e protocolos TLS..." "STEP"
# HTTP/2 (TLS e cleartext — este último só tem efeito em OS compatíveis) New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Force | Out-Null New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Name "EnableHttp2Tls" -PropertyType DWord -Value 1 -Force | Out-Null New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Name "EnableHttp2Cleartext" -PropertyType DWord -Value 1 -Force | Out-Null
# Schannel base $base = "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" $ensure = { param($proto,$role,$enabled,$disabledByDefault) $p = Join-Path $base "$proto\$role" New-Item -Path $p -Force | Out-Null New-ItemProperty -Path $p -Name "Enabled" -PropertyType DWord -Value $enabled -Force | Out-Null New-ItemProperty -Path $p -Name "DisabledByDefault" -PropertyType DWord -Value $disabledByDefault -Force | Out-Null }
# Desabilita legados: SSL2/SSL3/TLS1.0/TLS1.1 foreach($legacy in @("SSL 2.0","SSL 3.0","TLS 1.0","TLS 1.1")){ & $ensure $legacy "Server" 0 1 & $ensure $legacy "Client" 0 1 }
# Habilita modernos: TLS 1.2 (sempre), TLS 1.3 (se disponível no OS) & $ensure "TLS 1.2" "Server" 1 0 & $ensure "TLS 1.2" "Client" 1 0 try { & $ensure "TLS 1.3" "Server" 1 0 & $ensure "TLS 1.3" "Client" 1 0 } catch { Log "TLS 1.3 pode não estar disponível neste Windows. TLS 1.2 já habilitado." "WARN" }
Log "HTTP/2 habilitado; TLS 1.2/1.3 preferidos; SSL/TLS legados desabilitados. (Reinício do serviço HTTP/servidor recomendado)." "OK" }
# ---------- v2: HTTPS (Certify The Web se possível; senão self-signed) ---------- function Ensure-HttpsBinding { param([string]$Host,[string]$Site) Import-Module WebAdministration -ErrorAction SilentlyContinue
$managed = $false if((Test-Path $CertifyPath) -and -not [string]::IsNullOrWhiteSpace($CertifyManagedItemName)){ try{ Log "Certify encontrado. Tentando 'renew' do item '$CertifyManagedItemName'..." "STEP" & "$CertifyPath" renew --manageditem "$CertifyManagedItemName" | Out-Null Start-Sleep 3 Log "Tentando 'reapply' para reconfigurar bindings..." "STEP" & "$CertifyPath" reapply --manageditem "$CertifyManagedItemName" | Out-Null $managed = $true Log "Certify executado (renew/reapply). Se houver certificado válido, o binding HTTPS ficará OK." "OK" } catch { Log "Falha ao executar Certify CLI: $_" "WARN" } }
if(-not $managed){ # Fallback: self-signed + binding SNI (se Host) $cert = $null try { $dn = if([string]::IsNullOrWhiteSpace($Host)){"localhost"}else{$Host} $cert = New-SelfSignedCertificate -DnsName $dn -CertStoreLocation "cert:\LocalMachine\My" -FriendlyName "SelfSigned-$dn" -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(1) Log "Self-signed criado: $($cert.Thumbprint) ($dn)" "OK" } catch { Log "Falha ao criar self-signed: $_" "ERROR"; throw }
if($Force){ try{ Get-WebBinding -Name $Site -Protocol https -ErrorAction SilentlyContinue | Remove-WebBinding -ErrorAction SilentlyContinue }catch{} }
if([string]::IsNullOrWhiteSpace($Host)){ New-WebBinding -Name $Site -Protocol https -Port 443 -SslFlags 0 | Out-Null $certPath = "IIS:\SslBindings\0.0.0.0!443" if(Test-Path $certPath){ Remove-Item $certPath -Force -ErrorAction SilentlyContinue } New-Item -Path $certPath -Thumbprint $cert.Thumbprint -SSLFlags 0 | Out-Null Log "HTTPS 0.0.0.0:443 aplicado (self-signed) ao site '$Site'." "OK" } else { New-WebBinding -Name $Site -Protocol https -Port 443 -HostHeader $Host -SslFlags 1 | Out-Null $certPath = "IIS:\SslBindings\0.0.0.0!443!$Host" if(Test-Path $certPath){ Remove-Item $certPath -Force -ErrorAction SilentlyContinue } New-Item -Path $certPath -Thumbprint $cert.Thumbprint -SSLFlags 1 | Out-Null Log "HTTPS SNI $Host:443 aplicado (self-signed) ao site '$Site'." "OK" } } }
# ---------- v2: App Initialization em nível de site (garante pré-aquecimento) ---------- function Apply-AppInitialization { Import-Module WebAdministration -ErrorAction SilentlyContinue # Habilita o módulo (caso não esteja visível) try { Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/applicationInitialization" -name "." -value @{ doAppInitAfterRestart="true" } -ErrorAction SilentlyContinue Log "applicationInitialization habilitado no nível do servidor/appHost." "OK" } catch { Log "Falha ao habilitar applicationInitialization: $_" "WARN" } }
# ---------- Unsafe/System Takeover ---------- function Do-UnsafeSystemTakeover { Log "ATENÇÃO: executando modo UNSAFE (System32 ownership + UAC off)." "WARN" try{ reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA /t REG_DWORD /d 0 /f | Out-Null reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v ConsentPromptBehaviorAdmin /t REG_DWORD /d 0 /f | Out-Null Log "UAC desativado (requer reinicialização)." "OK" } catch { Log "Falha ao desativar UAC: $_" "ERROR" }
$sys32 = "$env:WINDIR\System32" try{ takeown /f $sys32 /r /d Y | Out-Null icacls $sys32 /grant "Administrators:(OI)(CI)F" /T /Q | Out-Null Log "System32: ownership e FullControl para Administrators." "OK" } catch { Log "Falha ao tomar posse/ACL System32: $_" "ERROR" }
$dism = Join-Path $sys32 "dism.exe" if(Test-Path $dism){ try{ takeown /f $dism | Out-Null icacls $dism /grant "Administrators:F" /Q | Out-Null icacls $dism /grant "IIS_IUSRS:RX" /Q | Out-Null Log "ACL de dism.exe ajustada." "OK" } catch { Log "Falha ao ajustar ACL do dism.exe: $_" "WARN" } }
if($DisableDefenderAlso){ try{ reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender" /v DisableAntiSpyware /t REG_DWORD /d 1 /f | Out-Null reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" /v DisableRealtimeMonitoring /t REG_DWORD /d 1 /f | Out-Null Log "Windows Defender desativado (política)." "WARN" } catch { Log "Falha ao desativar Defender: $_" "WARN" } } Log "UNSAFE concluído. ⚠️ Reinicie o servidor para efeito total." "WARN" }
function Ensure-Firewall { try{ New-NetFirewallRule -DisplayName "IIS HTTP 80" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80 -ErrorAction SilentlyContinue | Out-Null New-NetFirewallRule -DisplayName "IIS HTTPS 443" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443 -ErrorAction SilentlyContinue | Out-Null Log "Firewall liberado para 80/443." "OK" } catch { Log "Falha ao abrir firewall: $_" "WARN" } }
# ======================= # EXECUÇÃO # ======================= try{ Assert-AdminLocal
if($CreateLocalUser){ Ensure-LocalUser -User $LocalUser -Pass $LocalUserPassword -force:$Force } if($ConfigureIIS){ Ensure-IIS }
if($ConfigureIIS){ if($CreateLocalUser){ Configure-AppPoolBase -Pool $AppPoolName -User $LocalUser -Pass $LocalUserPassword } else{ Configure-AppPoolBase -Pool $AppPoolName } }
if($ConfigureWAS){ $awp = Get-AwpExe -ver $WebdevVersion if(!$awp){ throw "WD*awp.exe não encontrado. Verifique instalação do WebDev/WAS ($WebdevVersion)." } Configure-WebDevHandlers -awpExePath $awp.FullName }
if($CreateVirtualApp -and $ProjectName){ $userForPerms = ($CreateLocalUser) ? $LocalUser : "$env:USERNAME" $appPath = Ensure-VirtualApp -Name $ProjectName -Pool $AppPoolName -User $userForPerms }
if($Hardening){ Configure-AppPoolHardening -Pool $AppPoolName if($ProjectName){ if(-not $appPath){ Import-Module WebAdministration -ErrorAction SilentlyContinue $appPath = (Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue).PhysicalPath if([string]::IsNullOrWhiteSpace($appPath)){ $appPath = Join-Path "C:\inetpub\wwwroot" $ProjectName } } Write-HardenedWebConfig -PathToApp $appPath } Apply-AppInitialization Set-HTTP2-And-TLS-Policy Ensure-HttpsBinding -Host $Hostname -Site $SiteName }
if($Unsafe){ Do-UnsafeSystemTakeover } if($OpenFirewall){ Ensure-Firewall }
# Testes rapides try{ $r = Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing -TimeoutSec 10; Log "IIS responde (HTTP): $($r.StatusCode)" "OK" } catch { Log "IIS não respondeu em http://localhost" "WARN" } if($ProjectName){ try{ $u="http://localhost/{0}/test.html" -f $ProjectName; $r2=Invoke-WebRequest -Uri $u -UseBasicParsing -TimeoutSec 10; Log "App '$ProjectName' responde ($($r2.StatusCode)): $u" "OK" } catch { Log "App '$ProjectName' não respondeu." "WARN" } }
Log "Concluído." "OK" } catch{ Log "Falha: $_" "ERROR" throw }
⸻
Exemplos prontos
A) Hardening completo + app init + HTTPS com SNI + TLS moderno + HTTP/2
.\Setup-WAS-IIS-Variants-v2.ps1 ` -WebdevVersion 2025 ` -ConfigureIIS -ConfigureWAS ` -CreateLocalUser -LocalUser WebdevAdmin ` -CreateVirtualApp -ProjectName "MinhaApp" ` -Hardening -Hostname "app.seudominio.com.br" ` -OpenFirewall
Usando Certify The Web (se já existir o managed item)
.\Setup-WAS-IIS-Variants-v2.ps1 ` -ConfigureIIS -ConfigureWAS -Hardening ` -Hostname "portal.seudominio.com.br" ` -CertifyPath "C:\Program Files\CertifyTheWeb\Certify.exe" ` -CertifyManagedItemName "portal.seudominio.com.br"
Se o Certify não “reapply/renew”, o script cai para self-signed automaticamente e aplica o binding.
C) UNSAFE (laboratório apenas — toma posse do System32, desativa UAC, etc.)
.\Setup-WAS-IIS-Variants-v2.ps1 -Unsafe -DisableDefenderAlso
Reinicie o servidor depois.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 07:06 |
Entao, separei em dois scripts (hardening e unsafe) + atalhos .bat prontos. Você pode editar os parâmetros nos .bat e rodar em “Executar como administrador”.
⸻
1) Setup-WAS-Hardening.ps1
Script de endurecimento completo: IIS + WAS + Pool NoManagedCode + IdleTimeout 0 + Recycling off + AlwaysRunning + AutoStart + web.config rígido + HTTP/2 + TLS 1.2/1.3 + HTTPS (SNI com Certify The Web se existir; senão self-signed) + appInitialization + firewall.
#requires -RunAsAdministrator <# Setup-WAS-Hardening.ps1 IIS + WAS hardening completo: pool, web.config, HTTPS (SNI), HTTP/2, TLS moderno, app init. Use com: -ConfigureIIS -ConfigureWAS -CreateLocalUser -CreateVirtualApp -Hardening #> param( [ValidatePattern('^\d{4}$')] [string]$WebdevVersion = "2025", [string]$SiteName = "Default Web Site", [string]$AppPoolName = "WebdevAppPool", [string]$ProjectName = "", [string]$PhysicalPath = "", [string]$LocalUser = "WebdevAdmin", [SecureString]$LocalUserPassword, [switch]$CreateLocalUser, [switch]$ConfigureIIS, [switch]$ConfigureWAS, [switch]$CreateVirtualApp, [switch]$OpenFirewall, [switch]$Force, # HTTPS / Certify [string]$Hostname = "", [string]$CertifyPath = "C:\Program Files\CertifyTheWeb\Certify.exe", [string]$CertifyManagedItemName = "", [string]$ContactEmail = "" ) # ---------- infra de log ---------- $ErrorActionPreference = "Stop" $LogDir="C:\Logs"; if(!(Test-Path $LogDir)){ New-Item $LogDir -ItemType Directory -Force | Out-Null } $LogPath=Join-Path $LogDir ("WAS_HARDENING_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss")) function Log($m,$lvl="INFO"){ $ts=Get-Date -Format "yyyy-MM-dd HH:mm:ss"; $l="[$ts] [$lvl] $m"; Write-Host $l; Add-Content -Path $LogPath -Value $l } function Assert-AdminLocal{ $id=[Security.Principal.WindowsIdentity]::GetCurrent() $p =New-Object Security.Principal.WindowsPrincipal($id) if(-not $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){ Log "Execute como Administrador." "ERROR"; throw "Necessário administrador local." } $u=$id.Name if($u.Contains('\') -and -not $u.StartsWith($env:COMPUTERNAME+'\')){ Log "Usuário AD detectado ($u). Recomenda-se LOCAL para instalar/configurar WAS." "WARN" } } # ---------- IIS ---------- function Ensure-IIS{ Log "Instalando recursos do IIS..." "STEP" $features=@( "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures", "IIS-HttpErrors","IIS-HttpRedirect", "IIS-ApplicationDevelopment","IIS-NetFxExtensibility45","IIS-ASPNET45", "IIS-ISAPIExtensions","IIS-ISAPIFilter", "IIS-HealthAndDiagnostics","IIS-HttpLogging", "IIS-Security","IIS-RequestFiltering","IIS-Performance", "IIS-WebServerManagementTools","IIS-ManagementConsole", "IIS-IIS6ManagementCompatibility","IIS-Metabase" ) foreach($f in $features){ try{ Enable-WindowsOptionalFeature -Online -FeatureName $f -All -NoRestart -ErrorAction SilentlyContinue | Out-Null }catch{} } Import-Module WebAdministration -ErrorAction SilentlyContinue $site=Get-Website -Name $SiteName -ErrorAction SilentlyContinue if($site -and $site.State -ne "Started"){ Start-Website -Name $SiteName } Log "IIS e '$SiteName' prontos." "OK" } # ---------- Usuário local ---------- function Ensure-LocalUser{ param([string]$User,[SecureString]$Pass,[switch]$force) $ex=Get-LocalUser -Name $User -ErrorAction SilentlyContinue if($ex -and $force){ Log "Removendo $User" "WARN"; Remove-LocalUser -Name $User -ErrorAction SilentlyContinue; $ex=$null } if(!$ex){ if(!$Pass){ $Pass=Read-Host -AsSecureString -Prompt "Senha para $User (mín 8 chars)" } New-LocalUser -Name $User -Password $Pass -FullName "$User - WebDev Admin" -PasswordNeverExpires -UserMayNotChangePassword | Out-Null Log "Usuário criado: $User" "OK" } foreach($g in @("Administrators","IIS_IUSRS")){ try{ Add-LocalGroupMember -Group $g -Member $User -ErrorAction SilentlyContinue }catch{} } Log "Usuário '$User' em Administrators e IIS_IUSRS" "OK" } # ---------- App Pool ---------- function Configure-AppPoolBase{ param([string]$Pool,[string]$User,[SecureString]$Pass) Import-Module WebAdministration -ErrorAction SilentlyContinue if(-not (Get-IISAppPool -Name $Pool -ErrorAction SilentlyContinue)){ New-IISAppPool -Name $Pool | Out-Null } if($User -and $Pass){ $plain=[Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Pass)) Set-IISAppPool -Name $Pool -ProcessModel @{identityType="SpecificUser";userName=$User;password=$plain} } # hardening do pool Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{managedRuntimeVersion=""} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/processModel" -Value @{idleTimeout="00:00:00"; pingingEnabled="true"; pingInterval="00:00:30"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']/recycling/periodicRestart" -Value @{time="00:00:00"; privateMemory="0"} -PSPath IIS:\ -ErrorAction SilentlyContinue Set-WebConfiguration "/system.applicationHost/applicationPools/add[@name='$Pool']" -Value @{startMode="AlwaysRunning"} -PSPath IIS:\ -ErrorAction SilentlyContinue # aplica ao site e AutoStart no site Set-ItemProperty "IIS:\Sites\$SiteName" -Name applicationPool -Value $Pool Set-WebConfiguration "/system.applicationHost/sites/site[@name='$SiteName']" -Value @{ serverAutoStart="true" } -PSPath IIS:\ -ErrorAction SilentlyContinue Log "Pool '$Pool' endurecido + vinculado ao site '$SiteName'." "OK" } # ---------- WAS handlers ---------- function Get-AwpExe{ param([string]$ver) $roots=@( "C:\PC SOFT\WEBDEV $ver\Programs\Engine\Win64x86\AWP", "D:\PC SOFT\WEBDEV $ver\Programs\Engine\Win64x86\AWP", "C:\PC SOFT\WINDEV $ver\Programs\Engine\Win64x86\AWP", "D:\PC SOFT\WINDEV $ver\Programs\Engine\Win64x86\AWP", "C:\PC SOFT\WEBDEV $ver\Programs\Engine\Win32\AWP", "D:\PC SOFT\WEBDEV $ver\Programs\Engine\Win32\AWP" ) foreach($r in $roots){ if(Test-Path $r){ $f=Get-ChildItem $r -Filter "WD*awp.exe" -ErrorAction SilentlyContinue | Sort-Object Name -Descending | Select-Object -First 1; if($f){return $f} } } return $null } function Configure-WebDevHandlers{ param([string]$awpExePath) if(!(Test-Path $awpExePath)){ throw "WDxxawp.exe não encontrado ($awpExePath)" } Import-Module WebAdministration -ErrorAction SilentlyContinue $isapi=Get-WebConfiguration "system.webServer/security/isapiCgiRestriction/add[@path='$awpExePath']" -ErrorAction SilentlyContinue if(!$isapi){ Add-WebConfiguration "system.webServer/security/isapiCgiRestriction" -Value @{path=$awpExePath;allowed=$true;description=("WebDev AWP "+$WebdevVersion)} -ErrorAction SilentlyContinue } $hs=Get-WebConfiguration "system.webServer/handlers/add" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue | Where-Object { $_.path -like "*.awp" -or $_.path -like "*.awws" -or $_.path -like "*.wb" } foreach($h in $hs){ Remove-WebConfiguration "system.webServer/handlers/add[@name='$($h.name)']" -PSPath "IIS:\Sites\$SiteName" -ErrorAction SilentlyContinue } foreach($m in @(@{Ext=".awp";Name="WebDev-AWP"}, @{Ext=".awws";Name="WebDev-AWWS"}, @{Ext=".wb";Name="WebDev-WB"})){ Add-WebConfiguration "system.webServer/handlers" -PSPath "IIS:\Sites\$SiteName" -Value @{ name=$m.Name; path=("*{0}" -f $m.Ext); verb="*"; modules="IsapiModule"; scriptProcessor=$awpExePath; resourceType="File" } -ErrorAction SilentlyContinue } foreach($mm in @(@{ext=".awp";type="application/x-webdev-awp"}, @{ext=".awws";type="application/x-webdev-awws"}, @{ext=".wb";type="application/x-webdev-wb"})){ try{ Add-WebConfiguration "system.webServer/staticContent" -PSPath "IIS:\Sites\$SiteName" -Value @{fileExtension=$mm.ext;mimeType=$mm.type} -ErrorAction Stop }catch{} } Log "Handlers/MIME do WebDev configurados." "OK" } # ---------- Permissões e app virtual ---------- function Grant-Permissions($Path,$User){ if(!(Test-Path $Path)){ New-Item -ItemType Directory -Path $Path -Force | Out-Null } $acl=Get-Acl $Path $rules=@( New-Object System.Security.AccessControl.FileSystemAccessRule($User,"FullControl","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("IIS_IUSRS","Modify","ContainerInherit, ObjectInherit","None","Allow"), New-Object System.Security.AccessControl.FileSystemAccessRule("NETWORK SERVICE","ReadAndExecute","ContainerInherit, ObjectInherit","None","Allow") ) foreach($r in $rules){ $acl.SetAccessRule($r) } Set-Acl -Path $Path -AclObject $acl Log "Permissões aplicadas em '$Path'." "OK" } function Ensure-VirtualApp{ param([string]$Name,[string]$Pool,[string]$User) Import-Module WebAdministration -ErrorAction SilentlyContinue $phys = ($PhysicalPath) ? $PhysicalPath : (Join-Path "C:\inetpub\wwwroot" $Name) if($Force -and (Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ Remove-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue } if(!(Get-WebApplication -Site $SiteName -Name $Name -ErrorAction SilentlyContinue)){ New-WebApplication -Site $SiteName -Name $Name -PhysicalPath $phys -ApplicationPool $Pool | Out-Null } Grant-Permissions -Path $phys -User $User # web.config base (será substituído pelo hardening) @" "@ | Out-File -FilePath (Join-Path $phys "web.config") -Encoding UTF8 -Force " ✅ $Name online" | Out-File -FilePath (Join-Path $phys "test.html") -Encoding UTF8 -Force Log "App virtual '$Name' em $phys" "OK" return $phys } # ---------- web.config HARDENED + headers + appInitialization ---------- function Write-HardenedWebConfig{ param([string]$PathToApp) $cfg=@"
"@ $cfg | Out-File -FilePath (Join-Path $PathToApp "web.config") -Encoding UTF8 -Force Log "web.config endurecido aplicado." "OK" } # ---------- HTTP/2 + TLS 1.2/1.3 ---------- function Set-HTTP2-And-TLS-Policy{ Log "Aplicando HTTP/2 e TLS moderno..." "STEP" New-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Force | Out-Null New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Name "EnableHttp2Tls" -PropertyType DWord -Value 1 -Force | Out-Null New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\HTTP\Parameters" -Name "EnableHttp2Cleartext" -PropertyType DWord -Value 1 -Force | Out-Null $base="HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" function _set([string]$p,[string]$role,[int]$en,[int]$dd){ $k=Join-Path $base "$p\$role"; New-Item -Path $k -Force | Out-Null New-ItemProperty -Path $k -Name "Enabled" -PropertyType DWord -Value $en -Force | Out-Null New-ItemProperty -Path $k -Name "DisabledByDefault" -PropertyType DWord -Value $dd -Force | Out-Null } foreach($legacy in @("SSL 2.0","SSL 3.0","TLS 1.0","TLS 1.1")){ _set $legacy "Server" 0 1; _set $legacy "Client" 0 1 } _set "TLS 1.2" "Server" 1 0; _set "TLS 1.2" "Client" 1 0 try{ _set "TLS 1.3" "Server" 1 0; _set "TLS 1.3" "Client" 1 0 }catch{ Log "TLS 1.3 pode não existir neste Windows; TLS 1.2 OK." "WARN" } Log "HTTP/2 ativo; TLS 1.2/1.3 preferidos; legados desativados. (Reinício recomendado)." "OK" } # ---------- HTTPS (Certify se possível; fallback self-signed) ---------- function Ensure-HttpsBinding{ param([string]$Host,[string]$Site) Import-Module WebAdministration -ErrorAction SilentlyContinue $managed=$false if((Test-Path $CertifyPath) -and -not [string]::IsNullOrWhiteSpace($CertifyManagedItemName)){ try{ Log "Certify: renew '$CertifyManagedItemName'..." "STEP"; & "$CertifyPath" renew --manageditem "$CertifyManagedItemName" | Out-Null Start-Sleep 2 Log "Certify: reapply '$CertifyManagedItemName'..." "STEP"; & "$CertifyPath" reapply --manageditem "$CertifyManagedItemName" | Out-Null $managed=$true; Log "Certify executado (se certificado válido, binding será aplicado)." "OK" }catch{ Log "Falha Certify CLI: $_" "WARN" } } if(-not $managed){ $dn = if([string]::IsNullOrWhiteSpace($Host)){"localhost"}else{$Host} $cert=New-SelfSignedCertificate -DnsName $dn -CertStoreLocation "cert:\LocalMachine\My" -FriendlyName "SelfSigned-$dn" -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(1) if($Force){ try{ Get-WebBinding -Name $Site -Protocol https -ErrorAction SilentlyContinue | Remove-WebBinding -ErrorAction SilentlyContinue }catch{} } if([string]::IsNullOrWhiteSpace($Host)){ New-WebBinding -Name $Site -Protocol https -Port 443 -SslFlags 0 | Out-Null $p="IIS:\SslBindings\0.0.0.0!443"; if(Test-Path $p){ Remove-Item $p -Force -ErrorAction SilentlyContinue } New-Item -Path $p -Thumbprint $cert.Thumbprint -SSLFlags 0 | Out-Null } else { New-WebBinding -Name $Site -Protocol https -Port 443 -HostHeader $Host -SslFlags 1 | Out-Null $p="IIS:\SslBindings\0.0.0.0!443!$Host"; if(Test-Path $p){ Remove-Item $p -Force -ErrorAction SilentlyContinue } New-Item -Path $p -Thumbprint $cert.Thumbprint -SSLFlags 1 | Out-Null } Log "HTTPS aplicado com self-signed ($dn)." "OK" } } # ---------- appInitialization global ---------- function Apply-AppInitialization{ Import-Module WebAdministration -ErrorAction SilentlyContinue try{ Set-WebConfigurationProperty -pspath 'MACHINE/WEBROOT/APPHOST' -filter "system.webServer/applicationInitialization" -name "." -value @{ doAppInitAfterRestart="true" } -ErrorAction SilentlyContinue Log "applicationInitialization habilitado no appHost." "OK" }catch{ Log "Falha ao habilitar applicationInitialization: $_" "WARN" } } # ---------- Firewall ---------- function Ensure-Firewall{ try{ New-NetFirewallRule -DisplayName "IIS HTTP 80" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80 -ErrorAction SilentlyContinue | Out-Null New-NetFirewallRule -DisplayName "IIS HTTPS 443" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443 -ErrorAction SilentlyContinue | Out-Null Log "Firewall liberado 80/443." "OK" }catch{ Log "Falha ao abrir firewall: $_" "WARN" } } # ---------- exec ---------- try{ Assert-AdminLocal if($CreateLocalUser){ Ensure-LocalUser -User $LocalUser -Pass $LocalUserPassword -force:$Force } if($ConfigureIIS){ Ensure-IIS; Configure-AppPoolBase -Pool $AppPoolName -User ($CreateLocalUser ? $LocalUser : $null) -Pass $LocalUserPassword } if($ConfigureWAS){ $awp=Get-AwpExe -ver $WebdevVersion if(!$awp){ throw "WD*awp.exe não encontrado. Verifique instalação do WebDev/WAS ($WebdevVersion)." } Configure-WebDevHandlers -awpExePath $awp.FullName } if($CreateVirtualApp -and $ProjectName){ $userForPerms = ($CreateLocalUser) ? $LocalUser : "$env:USERNAME" $appPath = Ensure-VirtualApp -Name $ProjectName -Pool $AppPoolName -User $userForPerms } # Hardening de fato Configure-AppPoolBase -Pool $AppPoolName -User ($CreateLocalUser ? $LocalUser : $null) -Pass $LocalUserPassword if($ProjectName){ if(-not $appPath){ Import-Module WebAdministration -ErrorAction SilentlyContinue $appPath = (Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue).PhysicalPath if([string]::IsNullOrWhiteSpace($appPath)){ $appPath = Join-Path "C:\inetpub\wwwroot" $ProjectName } } Write-HardenedWebConfig -PathToApp $appPath } Apply-AppInitialization Set-HTTP2-And-TLS-Policy Ensure-HttpsBinding -Host $Hostname -Site $SiteName if($OpenFirewall){ Ensure-Firewall } # Testes rápidos try{ $r=Invoke-WebRequest -Uri "http://localhost" -UseBasicParsing -TimeoutSec 10; Log "IIS responde (HTTP): $($r.StatusCode)" "OK" }catch{ Log "IIS não respondeu em http://localhost" "WARN" } if($ProjectName){ try{ $u="http://localhost/{0}/test.html" -f $ProjectName; $r2=Invoke-WebRequest -Uri $u -UseBasicParsing -TimeoutSec 10; Log "App '$ProjectName' responde ($($r2.StatusCode)): $u" "OK" }catch{ Log "App '$ProjectName' não respondeu." "WARN" } } Log "Hardening concluído." "OK" }catch{ Log "Falha: $_" "ERROR"; throw }
⸻
2) Setup-WAS-Unsafe.ps1
Script perigoso (laboratório apenas): A) desativa UAC,
toma posse do System32, C) ajusta ACL do dism.exe e (opcional) D) desativa Defender. E) Incluí opção para abrir firewall.
#requires -RunAsAdministrator <# Setup-WAS-Unsafe.ps1 LAB APENAS. Reduz a segurança do Windows. Use somente quando indispensável e por curto período. #>
param( [switch]$DisableDefenderAlso, [switch]$OpenFirewall )
$ErrorActionPreference="Stop" $LogDir="C:\Logs"; if(!(Test-Path $LogDir)){ New-Item $LogDir -ItemType Directory -Force | Out-Null } $LogPath=Join-Path $LogDir ("WAS_UNSAFE_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss")) function Log($m,$lvl="INFO"){ $ts=Get-Date -Format "yyyy-MM-dd HH:mm:ss"; $l="[$ts] [$lvl] $m"; Write-Host $l; Add-Content -Path $LogPath -Value $l } function Assert-Admin{ $p=New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()); if(-not $p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)){ Log "Execute como Administrador." "ERROR"; throw } }
function Ensure-Firewall{ try{ New-NetFirewallRule -DisplayName "IIS HTTP 80" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 80 -ErrorAction SilentlyContinue | Out-Null New-NetFirewallRule -DisplayName "IIS HTTPS 443" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443 -ErrorAction SilentlyContinue | Out-Null Log "Firewall liberado 80/443." "OK" }catch{ Log "Falha ao abrir firewall: $_" "WARN" } }
try{ Assert-Admin Log "⚠️ MODO UNSAFE — alterações profundas de segurança. Use apenas em LAB." "WARN"
# 1) Desativar UAC (requer reinício) try{ reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v EnableLUA /t REG_DWORD /d 0 /f | Out-Null reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v ConsentPromptBehaviorAdmin /t REG_DWORD /d 0 /f | Out-Null Log "UAC desativado (reinicie o servidor)." "OK" }catch{ Log "Falha ao desativar UAC: $_" "ERROR" }
# 2) Tomar posse do System32 e conceder FullControl p/ Administrators $sys32="$env:WINDIR\System32" try{ takeown /f $sys32 /r /d Y | Out-Null icacls $sys32 /grant "Administrators:(OI)(CI)F" /T /Q | Out-Null Log "System32: ownership e FullControl para Administrators." "OK" }catch{ Log "Falha ao tomar posse/ACL System32: $_" "ERROR" }
# 3) Ajustar ACL de dism.exe $dism=Join-Path $sys32 "dism.exe" if(Test-Path $dism){ try{ takeown /f $dism | Out-Null icacls $dism /grant "Administrators:F" /Q | Out-Null icacls $dism /grant "IIS_IUSRS:RX" /Q | Out-Null Log "ACL de dism.exe ajustada." "OK" }catch{ Log "Falha ao ajustar ACL do dism.exe: $_" "WARN" } }
# 4) (Opcional) Desativar Defender if($DisableDefenderAlso){ try{ reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender" /v DisableAntiSpyware /t REG_DWORD /d 1 /f | Out-Null reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection" /v DisableRealtimeMonitoring /t REG_DWORD /d 1 /f | Out-Null Log "Windows Defender desativado (política)." "WARN" }catch{ Log "Falha ao desativar Defender: $_" "WARN" } }
if($OpenFirewall){ Ensure-Firewall }
Log "UNSAFE concluído. ⚠️ REINICIE o servidor para efeito total." "WARN" }catch{ Log "Falha: $_" "ERROR"; throw }
⸻
3) Atalhos .bat
Run-Hardening.bat
Altere os valores entre aspas conforme seu ambiente e salve no mesmo diretório dos .ps1.
@echo off setlocal set PS=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe
REM >>> EDITE AQUI <<< set WEBDEV_VERSION=2025 set SITE_NAME=Default Web Site set APPPOOL_NAME=WebdevAppPool set PROJECT_NAME=MinhaApp set LOCAL_USER=WebdevAdmin set HOSTNAME=app.seudominio.com.br set CERTIFY_PATH=C:\Program Files\CertifyTheWeb\Certify.exe set CERTIFY_ITEM=app.seudominio.com.br
"%PS%" -NoProfile -ExecutionPolicy Bypass -File ".\Setup-WAS-Hardening.ps1" ^ -WebdevVersion "%WEBDEV_VERSION%" ^ -SiteName "%SITE_NAME%" ^ -AppPoolName "%APPPOOL_NAME%" ^ -ProjectName "%PROJECT_NAME%" ^ -CreateLocalUser -LocalUser "%LOCAL_USER%" ^ -ConfigureIIS -ConfigureWAS -CreateVirtualApp ^ -Hostname "%HOSTNAME%" ^ -CertifyPath "%CERTIFY_PATH%" ^ -CertifyManagedItemName "%CERTIFY_ITEM%" ^ -OpenFirewall endlocal
Run-Unsafe.bat
Executa o modo perigoso (laboratório).
@echo off setlocal set PS=%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe "%PS%" -NoProfile -ExecutionPolicy Bypass -File ".\Setup-WAS-Unsafe.ps1" -DisableDefenderAlso -OpenFirewall endlocal
⸻
Como usar (passo a passo rápido) 1. Copie os quatro arquivos para uma pasta (ex.: C:\WAS-Setup\): • Setup-WAS-Hardening.ps1 • Setup-WAS-Unsafe.ps1 • Run-Hardening.bat • Run-Unsafe.bat 2. Clique com o direito em cada .bat → Executar como administrador. 3. Ajuste os parâmetros nos .bat (domínio, nomes, etc.). 4. No hardening, se você já usa Certify The Web e tem um Managed Item criado, preencha CERTIFY_ITEM; senão o script cria self-signed e aplica SNI (se HOSTNAME informado). 5. Após UNSAFE, reinicie o servidor.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/Mensaje modificado, 22,septiembre 2025 - 07:19 |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 07:17 |
O script agora apresenta um menu interativo para que você possa escolher as ações a serem executadas.
Como usar o script:
1. Salve o arquivo `Setup-WAS-IIS_v2.ps1` em seu computador. 2. Abra o PowerShell como administrador. 3. Navegue até o diretório onde você salvou o script. 4. Execute o script digitando: `.\Setup-WAS-IIS_v2.ps1` 5. Siga as opções do menu interativo para configurar o IIS/WAS ou realizar o backup.
Se precisar de alguma alteração ou refinamento após seus testes, por favor, me informe.
#requires -RunAsAdministrator <# .SYNOPSIS Configuração COMPLETA do IIS + W.A.S. (WebDev Application Server) com diagnósticos e backup. .DESCRIPTION - Instala recursos do IIS necessários ao WebDev/WAS - (Opcional) Cria usuário LOCAL dedicado e o coloca nos grupos Administrators e IIS_IUSRS - Configura Application Pool, ISAPI/Handlers (.awp/.awws/.wb), permissões NTFS - (Opcional) Faz correções para DISM funcionar quando exigido pelo WAS - Cria Aplicação Virtual e testa URL - Executa diagnóstico abrangente com resumo em % de prontidão - Realiza backup da configuração do IIS e dos sites.
Baseado em experiências reais e tópicos do fórum PC SOFT (checklist, erros de Virtual Directory, handlers e DISM). Autor: Adriano José Boller – PC SOFT Brasil | Versão 4.0 #>
# ========================= # Inicialização e utilitários # ========================= $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $global:StartedAt = Get-Date $LogDir = "C:\Logs" if (!(Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $LogPath = Join-Path $LogDir ("WAS_IIS_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss"))
function Write-Log(\[string\]$msg, \[ValidateSet("INFO","WARN","ERROR","OK","STEP")\]$level="INFO"){ $stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "\[{0}\] \[{1}\] {2}" -f $stamp, $level, $msg Write-Host $line Add-Content -Path $LogPath -Value $line }
function Confirm-Action(\[string\]$question){ $ans = Read-Host "$question (S/N)" return ($ans -match '^(s|S|y|Y)$') }
function Must-BeLocalAdmin { $id = \[Security.Principal.WindowsIdentity\]::GetCurrent() $pri = New-Object Security.Principal.WindowsPrincipal($id) if (-not $pri.IsInRole(\[Security.Principal.WindowsBuiltInRole\]::Administrator)) { Write-Log "Execute como Administrador." "ERROR"; throw "Necessário administrador local." } }
function Ensure-NotADUser { $u = \[Security.Principal.WindowsIdentity\]::GetCurrent().Name if ($u.Contains('\') -and -not $u.StartsWith($env:COMPUTERNAME+'\')) { Write-Log "Usuário do AD detectado ($u). Use usuário LOCAL administrativo." "ERROR" throw "Não execute com usuário do AD." } }
function Resolve-WebDevPaths { param(\[string\]$ver, \[string\]$InstallPathC, \[string\]$InstallPathD) $c = $InstallPathC -replace '(\{0\})', $ver $d = $InstallPathD -replace '(\{0\})', $ver $c = $c -replace 'WINDEV', 'WEBDEV' $d = $d -replace 'WINDEV', 'WEBDEV' $c2 = $InstallPathC -replace '(\{0\})', $ver $candidates = @( (Join-Path $c "Programs\Engine\Win64x86\AWP"), (Join-Path $d "Programs\Engine\Win64x86\AWP"), (Join-Path $c2 "Programs\Engine\Win64x86\AWP"), (Join-Path $c "Programs\Engine\Win32\AWP"), (Join-Path $d "Programs\Engine\Win32\AWP") ) | Get-Unique foreach($p in $candidates){ if(Test-Path $p){ return $p } } return $null }
function Get-AwpExe { param(\[string\]$ver, \[string\]$InstallPathC, \[string\]$InstallPathD) $awpFolder = Resolve-WebDevPaths -ver $ver -InstallPathC $InstallPathC -InstallPathD $InstallPathD if (!$awpFolder) { return $null } $files = Get-ChildItem -Path $awpFolder -Filter "WD*awp.exe" -ErrorAction SilentlyContinue | Sort-Object Name -Descending return $files | Select-Object -First 1 }
# ========================= # Funções de Ação # =========================
function Ensure-LocalWebdevUser { param(\[string\]$User, \[SecureString\]$Pass, \[switch\]$force) $exists = Get-LocalUser -Name $User -ErrorAction SilentlyContinue if ($exists -and $force){ Write-Log "Removendo usuário existente $User" "WARN" Remove-LocalUser -Name $User -ErrorAction SilentlyContinue $exists = $null } if (!$exists){ if (-not $Pass){ $Pass = Read-Host -AsSecureString -Prompt "Senha para $User (mín. 8 chars)" } New-LocalUser -Name $User -Password $Pass -FullName "$User - WebDev Admin" -PasswordNeverExpires -UserMayNotChangePassword | Out-Null Write-Log "Usuário criado: $User" "OK" } else { Write-Log "Usuário já existe: $User" "INFO" } foreach($grp in @("Administrators","IIS_IUSRS")){ try { Add-LocalGroupMember -Group $grp -Member $User -ErrorAction SilentlyContinue } catch {} } Write-Log "Usuário '$User' está nos grupos Administrators e IIS_IUSRS" "OK" }
function Ensure-IIS { param(\[string\]$SiteName) Write-Log "Instalando recursos IIS..." "STEP" $features = @( "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures", "IIS-HttpErrors","IIS-HttpRedirect", "IIS-ApplicationDevelopment","IIS-NetFxExtensibility45","IIS-ASPNET45", "IIS-ISAPIExtensions","IIS-ISAPIFilter", "IIS-HealthAndDiagnostics","IIS-HttpLogging", "IIS-Security","IIS-RequestFiltering", "IIS-Performance", "IIS-WebServerManagementTools","IIS-ManagementConsole", "IIS-IIS6ManagementCompatibility","IIS-Metabase" ) foreach($f in $features){ try { Enable-WindowsOptionalFeature -Online -FeatureName $f -All -NoRestart -ErrorAction SilentlyContinue | Out-Null } catch {} } Import-Module WebAdministration -ErrorAction SilentlyContinue $site = Get-Website -Name $SiteName -ErrorAction SilentlyContinue if (!$site) { Write-Log "Criando site padrão '$SiteName'..." "INFO" New-Website -Name $SiteName -Port 80 -PhysicalPath "C:\inetpub\wwwroot" | Out-Null $site = Get-Website -Name $SiteName } if ($site.State -ne "Started") { Write-Log "Iniciando site '$SiteName'..." "INFO" Start-Website -Name $SiteName } Write-Log "Recursos IIS instalados e site '$SiteName' garantido." "OK" }
function Configure-WAS { param(\[string\]$WebdevVersion, \[string\]$ProjectName, \[string\]$SiteName, \[string\]$LocalUser, \[SecureString\]$LocalUserPassword, \[switch\]$Force, \[switch\]$CopyDllsToBin, \[string\]$InstallPathC, \[string\]$InstallPathD)
Write-Log "Configurando WAS para WebDev $WebdevVersion..." "STEP"
$awpExe = Get-AwpExe -ver $WebdevVersion -InstallPathC $InstallPathC -InstallPathD $InstallPathD if (!$awpExe) { Write-Log "WDxxAWP.exe não encontrado para WebDev $WebdevVersion. Verifique InstallPathC/D." "ERROR" throw "WDxxAWP.exe não encontrado." } $awpExePath = $awpExe.FullName $awpFolder = $awpExe.DirectoryName Write-Log "WDxxAWP.exe encontrado em: $awpExePath" "INFO"
$appPoolName = "WebDevAppPool_{0}" -f $WebdevVersion $appPool = Get-WebAppPoolState -Name $appPoolName -ErrorAction SilentlyContinue if (!$appPool -or $Force) { if ($appPool) { Remove-WebAppPool -Name $appPoolName } Write-Log "Criando Application Pool '$appPoolName'..." "INFO" New-WebAppPool -Name $appPoolName -Force | Out-Null Set-ItemProperty IIS:\AppPools\$appPoolName -Name managedRuntimeVersion -Value "v4.0" Set-ItemProperty IIS:\AppPools\$appPoolName -Name enable32BitAppOnWin64 -Value $true Set-ItemProperty IIS:\AppPools\$appPoolName -Name processModel.identityType -Value "SpecificUser" Set-ItemProperty IIS:\AppPools\$appPoolName -Name processModel.userName -Value $LocalUser Set-ItemProperty IIS:\AppPools\$appPoolName -Name processModel.password -Value $LocalUserPassword Write-Log "Application Pool '$appPoolName' criado e configurado." "OK" } else { Write-Log "Application Pool '$appPoolName' já existe." "INFO" } if ((Get-WebAppPoolState -Name $appPoolName).Value -ne "Started") { Write-Log "Iniciando Application Pool '$appPoolName'..." "INFO" Start-WebAppPool -Name $appPoolName }
$handlers = @( @{Name="AWP-ISAPI"; Path="*.awp"; Verb="*"; Modules="IsapiModule"; ScriptProcessor=$awpExePath; ResourceType="Unspecified"; AllowPathInfo=$true}, @{Name="AWWS-ISAPI"; Path="*.awws"; Verb="*"; Modules="IsapiModule"; ScriptProcessor=$awpExePath; ResourceType="Unspecified"; AllowPathInfo=$true}, @{Name="WB-ISAPI"; Path="*.wb"; Verb="*"; Modules="IsapiModule"; ScriptProcessor=$awpExePath; ResourceType="Unspecified"; AllowPathInfo=$true} ) foreach($h in $handlers){ try { Add-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "system.webServer/handlers" -Name "." -Value $h -ErrorAction SilentlyContinue Write-Log "Handler '$($h.Name)' adicionado/verificado." "OK" } catch { Write-Log "Erro ao adicionar handler $($h.Name): $($_.Exception.Message)" "WARN" } }
Set-WebConfigurationProperty -PSPath "IIS:\" -Filter "system.webServer/security/isapiCgiRestriction" -Name "." -Value @{path=$awpExePath; allowed=$true; description="WebDev AWP Engine"} -ErrorAction SilentlyContinue Write-Log "ISAPI-DLL para $awpExePath habilitado." "OK"
$folders = @( $awpFolder, "C:\inetpub\wwwroot", "C:\PC SOFT\Shared" ) foreach($f in $folders){ if (Test-Path $f) { $acl = Get-Acl $f $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($LocalUser, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow") $acl.AddAccessRule($rule) Set-Acl $f $acl Write-Log "Permissões 'Modify' para '$LocalUser' na pasta '$f'." "OK" } else { Write-Log "Pasta '$f' não encontrada, pulando permissões." "WARN" } }
if ($CopyDllsToBin) { Write-Log "Copiando DLLs do engine para $awpFolder..." "INFO" $engineDlls = Get-ChildItem -Path (Join-Path $awpFolder "..\..") -Filter "*.dll" -Recurse -ErrorAction SilentlyContinue foreach ($dll in $engineDlls) { try { Copy-Item -Path $dll.FullName -Destination $awpFolder -Force -ErrorAction SilentlyContinue } catch { Write-Log "Erro ao copiar $($dll.Name): $($_.Exception.Message)" "WARN" } } Write-Log "Cópia de DLLs concluída." "OK" }
Write-Log "Configuração WAS concluída." "OK" }
function Create-VirtualApplication { param(\[string\]$WebdevVersion, \[string\]$ProjectName, \[string\]$SiteName, \[string\]$LocalUser, \[switch\]$Force, \[string\]$InstallPathC, \[string\]$InstallPathD)
if ([string]::IsNullOrEmpty($ProjectName)) { Write-Log "ProjectName é obrigatório para criar aplicação virtual." "ERROR" throw "ProjectName é obrigatório." }
Write-Log "Criando Aplicação Virtual para '$ProjectName'..." "STEP"
$awpExe = Get-AwpExe -ver $WebdevVersion -InstallPathC $InstallPathC -InstallPathD $InstallPathD if (!$awpExe) { Write-Log "WDxxAWP.exe não encontrado para WebDev $WebdevVersion." "ERROR" throw "WDxxAWP.exe não encontrado." } $awpFolder = $awpExe.DirectoryName
$appPoolName = "WebDevAppPool_{0}" -f $WebdevVersion $virtualPath = "/$ProjectName" $physicalPath = Join-Path $awpFolder "Projects\$ProjectName"
if (!(Test-Path $physicalPath)) { Write-Log "Caminho físico do projeto '$physicalPath' não encontrado." "ERROR" throw "Caminho físico do projeto não encontrado." }
$app = Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue if ($app -and $Force) { Write-Log "Removendo aplicação virtual existente '$ProjectName'." "WARN" Remove-WebApplication -Site $SiteName -Name $ProjectName $app = $null }
if (!$app) { Write-Log "Criando aplicação virtual '$virtualPath' apontando para '$physicalPath'..." "INFO" New-WebApplication -Name $ProjectName -Site $SiteName -PhysicalPath $physicalPath -ApplicationPool $appPoolName | Out-Null Write-Log "Aplicação virtual '$ProjectName' criada." "OK" } else { Write-Log "Aplicação virtual '$ProjectName' já existe." "INFO" }
Write-Log "Aplicação Virtual para '$ProjectName' configurada." "OK" }
function Run-Diagnostics { param(\[string\]$WebdevVersion, \[string\]$ProjectName, \[string\]$SiteName, \[string\]$LocalUser, \[string\]$InstallPathC, \[string\]$InstallPathD)
Write-Log "Iniciando diagnóstico..." "STEP" $results = @{} $totalChecks = 0 $passedChecks = 0
function Perform-Check(\[string\]$description, \[scriptblock\]$checkScript) { $totalChecks++ Write-Log "Verificando: $description" "INFO" try { $result = Invoke-Command -ScriptBlock $checkScript if ($result -eq $true) { Write-Log "PASS: $description" "OK" $passedChecks++ $results[$description] = "PASS" } else { Write-Log "FAIL: $description" "ERROR" $results[$description] = "FAIL" } } catch { Write-Log "ERRO ao verificar $description: $($_.Exception.Message)" "ERROR" $results[$description] = "ERROR" } }
Perform-Check "IIS está instalado e rodando" { (Get-Service W3SVC -ErrorAction SilentlyContinue).Status -eq "Running" }
Perform-Check "Site '$SiteName' está rodando" { (Get-Website -Name $SiteName -ErrorAction SilentlyContinue).State -eq "Started" }
$appPoolName = "WebDevAppPool_{0}" -f $WebdevVersion Perform-Check "Application Pool '$appPoolName' existe e está rodando" { $pool = Get-WebAppPoolState -Name $appPoolName -ErrorAction SilentlyContinue $pool -ne $null -and $pool.Value -eq "Started" }
$awpExe = Get-AwpExe -ver $WebdevVersion -InstallPathC $InstallPathC -InstallPathD $InstallPathD Perform-Check "WDxxAWP.exe encontrado" { $awpExe -ne $null }
$awpExePath = $awpExe.FullName Perform-Check "Handlers .awp, .awws, .wb registrados" { $handlersOk = $true $handlerPaths = @("*.awp", "*.awws", "*.wb") foreach ($hp in $handlerPaths) { $h = Get-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "system.webServer/handlers" -Name "." | Where-Object {$_.Path -eq $hp -and $_.ScriptProcessor -eq $awpExePath} if (!$h) { $handlersOk = $false; break } } $handlersOk }
Perform-Check "ISAPI-DLL para WDxxAWP.exe habilitado" { $isapiRestriction = Get-WebConfigurationProperty -PSPath "IIS:\" -Filter "system.webServer/security/isapiCgiRestriction" -Name "." | Where-Object {$_.path -eq $awpExePath -and $_.allowed -eq $true} $isapiRestriction -ne $null }
$awpFolder = $awpExe.DirectoryName Perform-Check "Permissões 'Modify' para '$LocalUser' na pasta '$awpFolder'" { if (Test-Path $awpFolder) { $acl = Get-Acl $awpFolder $ruleExists = $false foreach ($access in $acl.Access) { if ($access.IdentityReference.Value -eq $LocalUser -and $access.FileSystemRights -band \[System.Security.AccessControl.FileSystemRights\]::Modify) { $ruleExists = $true; break } } $ruleExists } else { $false } }
if (-not \[string\]::IsNullOrEmpty($ProjectName)) { Perform-Check "Aplicação Virtual '/$ProjectName' existe e aponta corretamente" { $app = Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue $app -ne $null -and $app.PhysicalPath -eq (Join-Path $awpFolder "Projects\$ProjectName") }
Perform-Check "URL da aplicação virtual 'http://localhost/$ProjectName/' acessível" { try { $url = "http://localhost/$ProjectName/" $response = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop $response.StatusCode -eq 200 } catch { $false } } }
Write-Log "\n--- Resumo do Diagnóstico ---" "INFO" foreach ($key in $results.Keys) { Write-Log "$key: $($results[$key])" "INFO" } $readiness = 0 if ($totalChecks -gt 0) { $readiness = \[Math\]::Round(($passedChecks / $totalChecks) * 100) } Write-Log "Prontidão Geral: $passedChecks de $totalChecks verificações passaram ($readiness%)" "OK" Write-Log "Diagnóstico concluído." "STEP" }
function Fix-Dism { Write-Log "Tentando corrigir acesso ao DISM..." "STEP" try { Set-Service TrustedInstaller -StartupType Manual -ErrorAction SilentlyContinue Start-Service TrustedInstaller -ErrorAction SilentlyContinue Write-Log "Serviço TrustedInstaller garantido." "OK" } catch { Write-Log "Erro ao garantir TrustedInstaller: $($_.Exception.Message)" "WARN" } Write-Log "Correção de DISM concluída (se aplicável)." "STEP" }
function Open-FirewallPorts { Write-Log "Abrindo portas de firewall (80, 443)..." "STEP" $ports = @(80, 443) foreach ($p in $ports) { try { New-NetFirewallRule -DisplayName "IIS Port $p" -Direction Inbound -LocalPort $p -Protocol TCP -Action Allow -ErrorAction SilentlyContinue | Out-Null Write-Log "Porta $p aberta no firewall." "OK" } catch { Write-Log "Erro ao abrir porta $p: $($_.Exception.Message)" "WARN" } } Write-Log "Configuração de firewall concluída." "STEP" }
function Backup-IISConfiguration { param( \[string\]$BackupPath = "C:\IIS_Backup", \[string\]$SiteName = "Default Web Site" )
Write-Log "Iniciando backup da configuração do IIS..." "STEP"
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $currentBackupDir = Join-Path $BackupPath "IIS_Config_$timestamp"
if (!(Test-Path $currentBackupDir)) { New-Item -Path $currentBackupDir -ItemType Directory -Force | Out-Null Write-Log "Diretório de backup criado: $currentBackupDir" "OK" } else { Write-Log "Diretório de backup já existe: $currentBackupDir" "INFO" }
try { $appcmdPath = Join-Path $env:SystemRoot "System32\inetsrv\appcmd.exe" if (Test-Path $appcmdPath) { Write-Log "Executando backup da configuração do IIS com appcmd..." "INFO" & $appcmdPath add backup "IIS_Config_$timestamp" | Out-Null Write-Log "Backup da configuração do IIS concluído com appcmd. Nome: IIS_Config_$timestamp" "OK"
$appcmdBackupLocation = Join-Path $env:SystemRoot "System32\inetsrv\backup\IIS_Config_$timestamp" if (Test-Path $appcmdBackupLocation) { Copy-Item -Path $appcmdBackupLocation -Destination $currentBackupDir -Recurse -Force | Out-Null Write-Log "Copiado backup do appcmd para $currentBackupDir" "OK" } }
Import-Module WebAdministration -ErrorAction SilentlyContinue $sites = Get-Website -Name $SiteName -ErrorAction SilentlyContinue if ($sites) { foreach ($site in $sites) { Write-Log "Processando site: $($site.Name)" "INFO" $siteConfigPath = Join-Path $currentBackupDir "Sites\$($site.Name)" if (!(Test-Path $siteConfigPath)) { New-Item -Path $siteConfigPath -ItemType Directory -Force | Out-Null }
$webConfig = Join-Path $site.PhysicalPath "web.config" if (Test-Path $webConfig) { Copy-Item -Path $webConfig -Destination $siteConfigPath -Force | Out-Null Write-Log "Copiado web.config do site $($site.Name) para $siteConfigPath" "OK" }
$applications = Get-WebApplication -Site $site.Name -ErrorAction SilentlyContinue foreach ($app in $applications) { Write-Log "Aplicação virtual: $($app.Path) -> $($app.PhysicalPath)" "INFO" } } }
Write-Log "Backup da configuração do IIS concluído com sucesso em $currentBackupDir" "OK" } catch { Write-Log "Erro durante o backup da configuração do IIS: $($_.Exception.Message)" "ERROR" } }
# ========================= # Execução Principal # ========================= function Main { Must-BeLocalAdmin Ensure-NotADUser
# Parâmetros $WebdevVersion = "2025" $ProjectName = "" $SiteName = "Default Web Site" $LocalUser = "WebdevAdmin" $LocalUserPassword = $null $InstallPathC = "C:\PC SOFT\WINDEV {0}" -f $WebdevVersion $InstallPathD = "D:\PC SOFT\WINDEV {0}" -f $WebdevVersion $Force = $false $CopyDllsToBin = $false
while ($true) { Write-Host "\nMenu de Configuração WAS/IIS" Write-Host "1. Criar Usuário Local Dedicado" Write-Host "2. Instalar e Configurar IIS" Write-Host "3. Configurar WAS (WebDev Application Server)" Write-Host "4. Criar Aplicação Virtual" Write-Host "5. Executar Diagnóstico Completo" Write-Host "6. Corrigir Acesso ao DISM" Write-Host "7. Abrir Portas do Firewall (80, 443)" Write-Host "8. Fazer Backup do IIS e Sites" Write-Host "9. Executar Todas as Etapas (Exceto Backup e DISM)" Write-Host "Q. Sair"
$choice = Read-Host "Escolha uma opção"
switch ($choice) { "1" { Ensure-LocalWebdevUser -User $LocalUser -Pass $LocalUserPassword -force:$Force } "2" { Ensure-IIS -SiteName $SiteName } "3" { Configure-WAS -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -LocalUserPassword $LocalUserPassword -Force:$Force -CopyDllsToBin:$CopyDllsToBin -InstallPathC $InstallPathC -InstallPathD $InstallPathD } "4" { Create-VirtualApplication -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -Force:$Force -InstallPathC $InstallPathC -InstallPathD $InstallPathD } "5" { Run-Diagnostics -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -InstallPathC $InstallPathC -InstallPathD $InstallPathD } "6" { Fix-Dism } "7" { Open-FirewallPorts } "8" { Backup-IISConfiguration -SiteName $SiteName } "9" { Ensure-LocalWebdevUser -User $LocalUser -Pass $LocalUserPassword -force:$Force Ensure-IIS -SiteName $SiteName Configure-WAS -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -LocalUserPassword $LocalUserPassword -Force:$Force -CopyDllsToBin:$CopyDllsToBin -InstallPathC $InstallPathC -InstallPathD $InstallPathD Create-VirtualApplication -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -Force:$Force -InstallPathC $InstallPathC -InstallPathD $InstallPathD Run-Diagnostics -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -InstallPathC $InstallPathC -InstallPathD $InstallPathD Open-FirewallPorts } "q" { return } default { Write-Host "Opção inválida." -ForegroundColor Red } } } }
# Executa a função principal Main
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 07:25 |
# Script de Validação e Configuração do IIS e WAS (WebDev Application Server)
## Visão Geral
Este script PowerShell, desenvolvido por Manus AI, é uma ferramenta abrangente para auxiliar na configuração, validação e manutenção do Microsoft Internet Information Server (IIS) e do WebDev Application Server (WAS) da PC SOFT. Ele foi criado para simplificar o processo de garantir que seu ambiente esteja corretamente configurado para hospedar sites, webservices e websockets desenvolvidos com as ferramentas PC SOFT, além de oferecer funcionalidades essenciais de backup e verificação de permissões.
## Funcionalidades Principais
O script oferece um menu interativo com as seguintes opções:
1. **Criar Usuário Local Dedicado**: Cria um usuário local no sistema operacional e o adiciona aos grupos `Administrators` e `IIS_IUSRS`, garantindo as permissões necessárias para o WAS operar corretamente. 2. **Instalar e Configurar IIS**: Garante que todos os recursos e funcionalidades do IIS necessários para o WebDev/WAS estejam instalados e configurados. Isso inclui a criação e inicialização de um site padrão, se necessário. 3. **Configurar WAS (WebDev Application Server)**: Realiza a configuração específica do WAS, incluindo a criação e configuração de um Application Pool dedicado, registro de Handlers (.awp, .awws, .wb) e habilitação de ISAPI-DLL. Também configura permissões NTFS nas pastas críticas e, opcionalmente, copia DLLs do engine para a pasta do AWP/BIN. 4. **Criar Aplicação Virtual**: Cria uma aplicação virtual no IIS para um projeto WebDev específico, apontando para o diretório físico do projeto e associando-o ao Application Pool do WAS. 5. **Executar Diagnóstico Completo**: Realiza uma série de verificações para garantir que o IIS e o WAS estejam funcionando corretamente. Isso inclui a verificação da instalação do IIS, status do site, Application Pool, presença do `WDxxAWP.exe`, registro de Handlers, habilitação de ISAPI-DLL e permissões NTFS. Um resumo detalhado é fornecido ao final. 6. **Corrigir Acesso ao DISM**: Tenta corrigir problemas de acesso ao serviço DISM, que podem ser necessários para a instalação de recursos do Windows. 7. **Abrir Portas do Firewall (80, 443)**: Configura o firewall do Windows para permitir o tráfego nas portas 80 (HTTP) e 443 (HTTPS), essenciais para o acesso a sites e webservices. 8. **Fazer Backup do IIS e Sites**: Realiza um backup da configuração do IIS usando o `appcmd.exe` e copia os arquivos `web.config` dos sites configurados para um diretório de backup especificado. Isso garante que você tenha uma cópia de segurança das configurações críticas. 9. **Executar Todas as Etapas (Exceto Backup e DISM)**: Executa sequencialmente as etapas de criação de usuário, instalação/configuração do IIS, configuração do WAS, criação de aplicação virtual, diagnóstico e abertura de portas do firewall. 10. **Sair**: Encerra o script.
## Requisitos
* Sistema Operacional Windows Server ou Desktop com IIS instalado ou com permissão para instalar. * PowerShell 5.1 ou superior. * Permissões de Administrador local para executar o script. * Módulos `WebAdministration` e `LocalAccounts` do PowerShell (geralmente disponíveis por padrão em sistemas Windows Server).
## Como Usar
1. **Baixe o script**: Salve o arquivo `Setup-WAS-IIS_v2.ps1` em um local seguro no seu servidor. 2. **Execute como Administrador**: Abra o PowerShell como Administrador. Isso é crucial, pois o script realiza operações que exigem privilégios elevados. 3. **Navegue até o diretório**: Use o comando `cd` para navegar até o diretório onde você salvou o script. Exemplo: ```powershell cd C:\Caminho\Para\Seu\Script ``` 4. **Execute o script**: Digite o seguinte comando e pressione Enter: ```powershell .\Setup-WAS-IIS_v2.ps1 ``` 5. **Interaja com o menu**: O script apresentará um menu interativo. Escolha a opção desejada digitando o número correspondente e pressionando Enter. Siga as instruções na tela para fornecer quaisquer informações adicionais (como senhas, nomes de projeto, etc.).
## Parâmetros Padrão
O script utiliza alguns parâmetros padrão que podem ser ajustados diretamente no código, se necessário, antes da execução. Estes incluem:
* `$WebdevVersion = "2025"`: Versão do WebDev (usado para identificar caminhos e Application Pools). * `$ProjectName = ""`: Nome do projeto WebDev para a aplicação virtual. Deve ser preenchido para a opção 4. * `$SiteName = "Default Web Site"`: Nome do site IIS a ser configurado. * `$LocalUser = "WebdevAdmin"`: Nome do usuário local a ser criado. * `$InstallPathC = "C:\PC SOFT\WINDEV {0}" -f $WebdevVersion`: Caminho de instalação padrão do PC SOFT na unidade C:. * `$InstallPathD = "D:\PC SOFT\WINDEV {0}" -f $WebdevVersion`: Caminho de instalação padrão do PC SOFT na unidade D:. * `$Force = $false`: Define se artefatos existentes (usuários, Application Pools, etc.) devem ser recriados/removidos e criados novamente. * `$CopyDllsToBin = $false`: Define se as DLLs do engine devem ser copiadas para a pasta do AWP/BIN.
## Verificações de Permissão e Tratamento de Erros
O script inclui verificações iniciais para garantir que ele seja executado com privilégios de administrador local. Ele também alerta se um usuário de domínio (Active Directory) estiver sendo usado, pois isso pode levar a problemas de permissão complexos em alguns ambientes. Blocos `try-catch` foram implementados em várias funções para capturar e registrar erros, fornecendo mensagens informativas ao usuário.
## Considerações Importantes
* **Ambiente de Teste**: Recomenda-se testar este script em um ambiente de desenvolvimento ou homologação antes de aplicá-lo em produção. * **Backup**: Embora o script inclua uma função de backup, é sempre aconselhável ter backups adicionais e completos do sistema antes de realizar alterações significativas na configuração do servidor. * **Personalização**: O script pode ser personalizado para atender a necessidades específicas do seu ambiente. No entanto, faça-o com cautela e compreendendo as alterações.
## Autor
Manus AI
## Referências
* [Super script para validar Internet Information Server (MSIIS)](https://forum.pcsoft.fr/fr-FR/pcsoft.br.windev/5311-super-script-para-validar-internet-information-server-msiis-5314/read.awp)
#requires -RunAsAdministrator <# .SYNOPSIS Configuração COMPLETA do IIS + W.A.S. (WebDev Application Server) com diagnósticos e backup. .DESCRIPTION - Instala recursos do IIS necessários ao WebDev/WAS - (Opcional) Cria usuário LOCAL dedicado e o coloca nos grupos Administrators e IIS_IUSRS - Configura Application Pool, ISAPI/Handlers (.awp/.awws/.wb), permissões NTFS - (Opcional) Faz correções para DISM funcionar quando exigido pelo WAS - Cria Aplicação Virtual e testa URL - Executa diagnóstico abrangente com resumo em % de prontidão - Realiza backup da configuração do IIS e dos sites. - Inclui verificações de permissão e avisos claros para o usuário.
Baseado em experiências reais e tópicos do fórum PC SOFT (checklist, erros de Virtual Directory, handlers e DISM). Autor: Adriano José Boller – PC SOFT Brasil | Versão 4.1 #>
# ========================= # Inicialização e utilitários # ========================= $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $global:StartedAt = Get-Date $LogDir = "C:\Logs" if (!(Test-Path $LogDir)) { New-Item -Path $LogDir -ItemType Directory -Force | Out-Null } $LogPath = Join-Path $LogDir ("WAS_IIS_{0}.log" -f (Get-Date -Format "yyyyMMdd_HHmmss"))
function Write-Log(\[string\]$msg, \[ValidateSet("INFO","WARN","ERROR","OK","STEP")\]$level="INFO"){ $stamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "\[{0}\] \[{1}\] {2}" -f $stamp, $level, $msg Write-Host $l Add-Content -Path $LogPath -Value $l }
function Confirm-Action(\[string\]$question){ $ans = Read-Host "$question (S/N)" return ($ans -match '^(s|S|y|Y)$') }
function Must-BeLocalAdmin { $id = \[Security.Principal.WindowsIdentity\]::GetCurrent() $p = New-Object Security.Principal.WindowsPrincipal($id) if (-not $p.IsInRole(\[Security.Principal.WindowsBuiltInRole\]::Administrator)) { Write-Log "Execute como Administrador." "ERROR"; throw "Necessário administrador local." } }
function Ensure-NotADUser { $u = \[Security.Principal.WindowsIdentity\]::GetCurrent().Name if ($u.Contains('\\') -and -not $u.StartsWith($env:COMPUTERNAME+'\\')) { Write-Log "Usuário do AD detectado ($u). Recomenda-se usar usuário LOCAL administrativo." "WARN" # throw "Não execute com usuário do AD para evitar problemas de permissão complexos." } }
function Resolve-WebDevPaths { param(\[string\]$ver, \[string\]$InstallPathC, \[string\]$InstallPathD) $c = $InstallPathC -replace '(\{0\})', $ver $d = $InstallPathD -replace '(\{0\})', $ver $c = $c -replace 'WINDEV', 'WEBDEV' $d = $d -replace 'WINDEV', 'WEBDEV' $c2 = $InstallPathC -replace '(\{0\})', $ver $candidates = @( (Join-Path $c "Programs\Engine\Win64x86\AWP"), (Join-Path $d "Programs\Engine\Win64x86\AWP"), (Join-Path $c2 "Programs\Engine\Win64x86\AWP"), (Join-Path $c "Programs\Engine\Win32\AWP"), (Join-Path $d "Programs\Engine\Win32\AWP") ) | Get-Unique foreach($p in $candidates){ if(Test-Path $p){ return $p } } return $null }
function Get-AwpExe { param(\[string\]$ver, \[string\]$InstallPathC, \[string\]$InstallPathD) $awpFolder = Resolve-WebDevPaths -ver $ver -InstallPathC $InstallPathC -InstallPathD $InstallPathD if (!$awpFolder) { return $null } $files = Get-ChildItem -Path $awpFolder -Filter "WD*awp.exe" -ErrorAction SilentlyContinue | Sort-Object Name -Descending return $files | Select-Object -First 1 }
# ========================= # Funções de Ação # =========================
function Ensure-LocalWebdevUser { param(\[string\]$User, \[SecureString\]$Pass, \[switch\]$force) Write-Log "Verificando/Criando usuário local $User..." "STEP" $exists = Get-LocalUser -Name $User -ErrorAction SilentlyContinue if ($exists -and $force){ Write-Log "Removendo usuário existente $User" "WARN" Remove-LocalUser -Name $User -ErrorAction SilentlyContinue $exists = $null } if (!$exists){ if (-not $Pass){ $Pass = Read-Host -AsSecureString -Prompt "Senha para $User (mín. 8 chars)" } New-LocalUser -Name $User -Password $Pass -FullName "$User - WebDev Admin" -PasswordNeverExpires -UserMayNotChangePassword | Out-Null Write-Log "Usuário criado: $User" "OK" } else { Write-Log "Usuário já existe: $User" "INFO" } foreach($grp in @("Administrators","IIS_IUSRS")){ try { Add-LocalGroupMember -Group $grp -Member $User -ErrorAction SilentlyContinue } catch { Write-Log "Erro ao adicionar $User ao grupo $grp: $($_.Exception.Message)" "WARN" } } Write-Log "Usuário '$User' em Administrators e IIS_IUSRS." "OK" }
function Ensure-IIS { param(\[string\]$SiteName) Write-Log "Instalando recursos IIS..." "STEP" $features = @( "IIS-WebServerRole","IIS-WebServer","IIS-CommonHttpFeatures", "IIS-HttpErrors","IIS-HttpRedirect", "IIS-ApplicationDevelopment","IIS-NetFxExtensibility45","IIS-ASPNET45", "IIS-ISAPIExtensions","IIS-ISAPIFilter", "IIS-HealthAndDiagnostics","IIS-HttpLogging", "IIS-Security","IIS-RequestFiltering", "IIS-Performance", "IIS-WebServerManagementTools","IIS-ManagementConsole", "IIS-IIS6ManagementCompatibility","IIS-Metabase" ) foreach($f in $features){ try { Enable-WindowsOptionalFeature -Online -FeatureName $f -All -NoRestart -ErrorAction SilentlyContinue | Out-Null } catch { Write-Log "Erro ao instalar recurso IIS $f: $($_.Exception.Message)" "ERROR" } } Import-Module WebAdministration -ErrorAction SilentlyContinue $site = Get-Website -Name $SiteName -ErrorAction SilentlyContinue if (!$site) { Write-Log "Criando site padrão '$SiteName'..." "INFO" New-Website -Name $SiteName -Port 80 -PhysicalPath "C:\inetpub\wwwroot" | Out-Null $site = Get-Website -Name $SiteName } if ($site.State -ne "Started") { Write-Log "Iniciando site '$SiteName'..." "INFO" Start-Website -Name $SiteName } Write-Log "Recursos IIS instalados e site '$SiteName' garantido." "OK" }
function Configure-WAS { param(\[string\]$WebdevVersion, \[string\]$ProjectName, \[string\]$SiteName, \[string\]$LocalUser, \[SecureString\]$LocalUserPassword, \[switch\]$Force, \[switch\]$CopyDllsToBin, \[string\]$InstallPathC, \[string\]$InstallPathD)
Write-Log "Configurando WAS para WebDev $WebdevVersion..." "STEP"
$awpExe = Get-AwpExe -ver $WebdevVersion -InstallPathC $InstallPathC -InstallPathD $InstallPathD if (!$awpExe) { Write-Log "WDxxAWP.exe não encontrado para WebDev $WebdevVersion. Verifique InstallPathC/D." "ERROR" throw "WDxxAWP.exe não encontrado." } $awpExePath = $awpExe.FullName $awpFolder = $awpExe.DirectoryName Write-Log "WDxxAWP.exe encontrado em: $awpExePath" "INFO"
$appPoolName = "WebDevAppPool_{0}" -f $WebdevVersion $appPool = Get-WebAppPoolState -Name $appPoolName -ErrorAction SilentlyContinue if (!$appPool -or $Force) { if ($appPool) { Remove-WebAppPool -Name $appPoolName } Write-Log "Criando Application Pool '$appPoolName'..." "INFO" New-WebAppPool -Name $appPoolName -Force | Out-Null Set-ItemProperty IIS:\AppPools\$appPoolName -Name managedRuntimeVersion -Value "v4.0" Set-ItemProperty IIS:\AppPools\$appPoolName -Name enable32BitAppOnWin64 -Value $true Set-ItemProperty IIS:\AppPools\$appPoolName -Name processModel.identityType -Value "SpecificUser" Set-ItemProperty IIS:\AppPools\$appPoolName -Name processModel.userName -Value $LocalUser Set-ItemProperty IIS:\AppPools\$appPoolName -Name processModel.password -Value $LocalUserPassword Write-Log "Application Pool '$appPoolName' criado e configurado." "OK" } else { Write-Log "Application Pool '$appPoolName' já existe." "INFO" } if ((Get-WebAppPoolState -Name $appPoolName).Value -ne "Started") { Write-Log "Iniciando Application Pool '$appPoolName'..." "INFO" Start-WebAppPool -Name $appPoolName }
$handlers = @( @{Name="AWP-ISAPI"; Path="*.awp"; Verb="*"; Modules="IsapiModule"; ScriptProcessor=$awpExePath; ResourceType="Unspecified"; AllowPathInfo=$true}, @{Name="AWWS-ISAPI"; Path="*.awws"; Verb="*"; Modules="IsapiModule"; ScriptProcessor=$awpExePath; ResourceType="Unspecified"; AllowPathInfo=$true}, @{Name="WB-ISAPI"; Path="*.wb"; Verb="*"; Modules="IsapiModule"; ScriptProcessor=$awpExePath; ResourceType="Unspecified"; AllowPathInfo=$true} ) foreach($h in $handlers){ try { Add-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "system.webServer/handlers" -Name "." -Value $h -ErrorAction SilentlyContinue Write-Log "Handler '$($h.Name)' adicionado/verificado." "OK" } catch { Write-Log "Erro ao adicionar handler $($h.Name): $($_.Exception.Message)" "WARN" } }
Set-WebConfigurationProperty -PSPath "IIS:\" -Filter "system.webServer/security/isapiCgiRestriction" -Name "." -Value @{path=$awpExePath; allowed=$true; description="WebDev AWP Engine"} -ErrorAction SilentlyContinue Write-Log "ISAPI-DLL para $awpExePath habilitado." "OK"
$folders = @( $awpFolder, "C:\inetpub\wwwroot", "C:\PC SOFT\Shared" ) foreach($f in $folders){ if (Test-Path $f) { $acl = Get-Acl $f $rule = New-Object System.Security.AccessControl.FileSystemAccessRule($LocalUser, "Modify", "ContainerInherit,ObjectInherit", "None", "Allow") $acl.AddAccessRule($rule) Set-Acl $f $acl Write-Log "Permissões 'Modify' para '$LocalUser' na pasta '$f'." "OK" } else { Write-Log "Pasta '$f' não encontrada, pulando permissões." "WARN" } }
if ($CopyDllsToBin) { Write-Log "Copiando DLLs do engine para $awpFolder..." "INFO" $engineDlls = Get-ChildItem -Path (Join-Path $awpFolder "..\..") -Filter "*.dll" -Recurse -ErrorAction SilentlyContinue foreach ($dll in $engineDlls) { try { Copy-Item -Path $dll.FullName -Destination $awpFolder -Force -ErrorAction SilentlyContinue } catch { Write-Log "Erro ao copiar $($dll.Name): $($_.Exception.Message)" "WARN" } } Write-Log "Cópia de DLLs concluída." "OK" }
Write-Log "Configuração WAS concluída." "OK" }
function Create-VirtualApplication { param(\[string\]$WebdevVersion, \[string\]$ProjectName, \[string\]$SiteName, \[string\]$LocalUser, \[switch\]$Force, \[string\]$InstallPathC, \[string\]$InstallPathD)
if ([string]::IsNullOrEmpty($ProjectName)) { Write-Log "ProjectName é obrigatório para criar aplicação virtual." "ERROR" throw "ProjectName é obrigatório." }
Write-Log "Criando Aplicação Virtual para '$ProjectName'..." "STEP"
$awpExe = Get-AwpExe -ver $WebdevVersion -InstallPathC $InstallPathC -InstallPathD $InstallPathD if (!$awpExe) { Write-Log "WDxxAWP.exe não encontrado para WebDev $WebdevVersion." "ERROR" throw "WDxxAWP.exe não encontrado." } $awpFolder = $awpExe.DirectoryName
$appPoolName = "WebDevAppPool_{0}" -f $WebdevVersion $virtualPath = "/$ProjectName" $physicalPath = Join-Path $awpFolder "Projects\$ProjectName"
if (!(Test-Path $physicalPath)) { Write-Log "Caminho físico do projeto '$physicalPath' não encontrado." "ERROR" throw "Caminho físico do projeto não encontrado." }
$app = Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue if ($app -and $Force) { Write-Log "Removendo aplicação virtual existente '$ProjectName'." "WARN" Remove-WebApplication -Site $SiteName -Name $ProjectName $app = $null }
if (!$app) { Write-Log "Criando aplicação virtual '$virtualPath' apontando para '$physicalPath'..." "INFO" New-WebApplication -Name $ProjectName -Site $SiteName -PhysicalPath $physicalPath -ApplicationPool $appPoolName | Out-Null Write-Log "Aplicação virtual '$ProjectName' criada." "OK" } else { Write-Log "Aplicação virtual '$ProjectName' já existe." "INFO" }
Write-Log "Aplicação Virtual para '$ProjectName' configurada." "OK" }
function Run-Diagnostics { param(\[string\]$WebdevVersion, \[string\]$ProjectName, \[string\]$SiteName, \[string\]$LocalUser, \[string\]$InstallPathC, \[string\]$InstallPathD)
Write-Log "Iniciando diagnóstico..." "STEP" $results = @{} $totalChecks = 0 $passedChecks = 0
function Perform-Check(\[string\]$description, \[scriptblock\]$checkScript) { $totalChecks++ Write-Log "Verificando: $description" "INFO" try { $result = Invoke-Command -ScriptBlock $checkScript if ($result -eq $true) { Write-Log "PASS: $description" "OK" $passedChecks++ $results[$description] = "PASS" } else { Write-Log "FAIL: $description" "ERROR" $results[$description] = "FAIL" } } catch { Write-Log "ERRO ao verificar $description: $($_.Exception.Message)" "ERROR" $results[$description] = "ERROR" } }
Perform-Check "IIS está instalado e rodando" { (Get-Service W3SVC -ErrorAction SilentlyContinue).Status -eq "Running" }
Perform-Check "Site '$SiteName' está rodando" { (Get-Website -Name $SiteName -ErrorAction SilentlyContinue).State -eq "Started" }
$appPoolName = "WebDevAppPool_{0}" -f $WebdevVersion Perform-Check "Application Pool '$appPoolName' existe e está rodando" { $pool = Get-WebAppPoolState -Name $appPoolName -ErrorAction SilentlyContinue $pool -ne $null -and $pool.Value -eq "Started" }
$awpExe = Get-AwpExe -ver $WebdevVersion -InstallPathC $InstallPathC -InstallPathD $InstallPathD Perform-Check "WDxxAWP.exe encontrado" { $awpExe -ne $null }
$awpExePath = $awpExe.FullName Perform-Check "Handlers .awp, .awws, .wb registrados" { $handlersOk = $true $handlerPaths = @("*.awp", "*.awws", "*.wb") foreach ($hp in $handlerPaths) { $h = Get-WebConfigurationProperty -PSPath "IIS:\Sites\$SiteName" -Filter "system.webServer/handlers" -Name "." | Where-Object {$_.Path -eq $hp -and $_.ScriptProcessor -eq $awpExePath} if (!$h) { $handlersOk = $false; break } } $handlersOk }
Perform-Check "ISAPI-DLL para WDxxAWP.exe habilitado" { $isapiRestriction = Get-WebConfigurationProperty -PSPath "IIS:\" -Filter "system.webServer/security/isapiCgiRestriction" -Name "." | Where-Object {$_.path -eq $awpExePath -and $_.allowed -eq $true} $isapiRestriction -ne $null }
$awpFolder = $awpExe.DirectoryName Perform-Check "Permissões 'Modify' para '$LocalUser' na pasta '$awpFolder'" { if (Test-Path $awpFolder) { $acl = Get-Acl $awpFolder $ruleExists = $false foreach ($access in $acl.Access) { if ($access.IdentityReference.Value -eq $LocalUser -and $access.FileSystemRights -band \[System.Security.AccessControl.FileSystemRights\]::Modify) { $ruleExists = $true; break } } $ruleExists } else { $false } }
if (-not \[string\]::IsNullOrEmpty($ProjectName)) { Perform-Check "Aplicação Virtual '/$ProjectName' existe e aponta corretamente" { $app = Get-WebApplication -Site $SiteName -Name $ProjectName -ErrorAction SilentlyContinue $app -ne $null -and $app.PhysicalPath -eq (Join-Path $awpFolder "Projects\$ProjectName") }
Perform-Check "URL da aplicação virtual 'http://localhost/$ProjectName/' acessível" { try { $url = "http://localhost/$ProjectName/" $response = Invoke-WebRequest -Uri $url -UseBasicParsing -ErrorAction Stop $response.StatusCode -eq 200 } catch { $false } } }
Write-Log "\n--- Resumo do Diagnóstico ---" "INFO" foreach ($key in $results.Keys) { Write-Log "$key: $($results[$key])" "INFO" } $readiness = 0 if ($totalChecks -gt 0) { $readiness = \[Math\]::Round(($passedChecks / $totalChecks) * 100) } Write-Log "Prontidão Geral: $passedChecks de $totalChecks verificações passaram ($readiness%)" "OK" Write-Log "Diagnóstico concluído." "STEP" }
function Fix-Dism { Write-Log "Tentando corrigir acesso ao DISM..." "STEP" try { Set-Service TrustedInstaller -StartupType Manual -ErrorAction SilentlyContinue Start-Service TrustedInstaller -ErrorAction SilentlyContinue Write-Log "Serviço TrustedInstaller garantido." "OK" } catch { Write-Log "Erro ao garantir TrustedInstaller: $($_.Exception.Message)" "WARN" } Write-Log "Correção de DISM concluída (se aplicável)." "STEP" }
function Open-FirewallPorts { Write-Log "Abrindo portas de firewall (80, 443)..." "STEP" $ports = @(80, 443) foreach ($p in $ports) { try { New-NetFirewallRule -DisplayName "IIS Port $p" -Direction Inbound -LocalPort $p -Protocol TCP -Action Allow -ErrorAction SilentlyContinue | Out-Null Write-Log "Porta $p aberta no firewall." "OK" } catch { Write-Log "Erro ao abrir porta $p: $($_.Exception.Message)" "WARN" } } Write-Log "Configuração de firewall concluída." "STEP" }
function Backup-IISConfiguration { param( \[string\]$BackupPath = "C:\IIS_Backup", \[string\]$SiteName = "Default Web Site" )
Write-Log "Iniciando backup da configuração do IIS..." "STEP"
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss" $currentBackupDir = Join-Path $BackupPath "IIS_Config_$timestamp"
if (!(Test-Path $currentBackupDir)) { New-Item -Path $currentBackupDir -ItemType Directory -Force | Out-Null Write-Log "Diretório de backup criado: $currentBackupDir" "OK" } else { Write-Log "Diretório de backup já existe: $currentBackupDir" "INFO" }
try { $appcmdPath = Join-Path $env:SystemRoot "System32\inetsrv\appcmd.exe" if (Test-Path $appcmdPath) { Write-Log "Executando backup da configuração do IIS com appcmd..." "INFO" & $appcmdPath add backup "IIS_Config_$timestamp" | Out-Null Write-Log "Backup da configuração do IIS concluído com appcmd. Nome: IIS_Config_$timestamp" "OK"
$appcmdBackupLocation = Join-Path $env:SystemRoot "System32\inetsrv\backup\IIS_Config_$timestamp" if (Test-Path $appcmdBackupLocation) { Copy-Item -Path $appcmdBackupLocation -Destination $currentBackupDir -Recurse -Force | Out-Null Write-Log "Copiado backup do appcmd para $currentBackupDir" "OK" } else { Write-Log "Não foi possível encontrar o backup do appcmd em $appcmdBackupLocation" "WARN" } } else { Write-Log "appcmd.exe não encontrado em $appcmdPath. Não foi possível fazer backup da configuração do IIS." "WARN" }
Import-Module WebAdministration -ErrorAction SilentlyContinue $sites = Get-Website -Name $SiteName -ErrorAction SilentlyContinue if ($sites) { foreach ($site in $sites) { Write-Log "Processando site: $($site.Name)" "INFO" $siteConfigPath = Join-Path $currentBackupDir "Sites\$($site.Name)" if (!(Test-Path $siteConfigPath)) { New-Item -Path $siteConfigPath -ItemType Directory -Force | Out-Null }
$webConfig = Join-Path $site.PhysicalPath "web.config" if (Test-Path $webConfig) { Copy-Item -Path $webConfig -Destination $siteConfigPath -Force | Out-Null Write-Log "Copiado web.config do site $($site.Name) para $siteConfigPath" "OK" } else { Write-Log "web.config não encontrado para o site $($site.Name) em $($site.PhysicalPath)" "INFO" }
$applications = Get-WebApplication -Site $site.Name -ErrorAction SilentlyContinue foreach ($app in $applications) { Write-Log "Aplicação virtual: $($app.Path) -> $($app.PhysicalPath)" "INFO" } } } else { Write-Log "Nenhum site encontrado com o nome '$SiteName'." "WARN" }
Write-Log "Backup da configuração do IIS concluído com sucesso em $currentBackupDir" "OK" } catch { Write-Log "Erro durante o backup da configuração do IIS: $($_.Exception.Message)" "ERROR" } }
# ========================= # Execução Principal # ========================= function Main { Must-BeLocalAdmin Ensure-NotADUser
# Parâmetros padrão $WebdevVersion = "2025" $ProjectName = "" $SiteName = "Default Web Site" $LocalUser = "WebdevAdmin" $LocalUserPassword = $null $InstallPathC = "C:\PC SOFT\WINDEV {0}" -f $WebdevVersion $InstallPathD = "D:\PC SOFT\WINDEV {0}" -f $WebdevVersion $Force = $false $CopyDllsToBin = $false
while ($true) { Write-Host "\nMenu de Configuração WAS/IIS" Write-Host "1. Criar Usuário Local Dedicado" Write-Host "2. Instalar e Configurar IIS" Write-Host "3. Configurar WAS (WebDev Application Server)" Write-Host "4. Criar Aplicação Virtual" Write-Host "5. Executar Diagnóstico Completo" Write-Host "6. Corrigir Acesso ao DISM" Write-Host "7. Abrir Portas do Firewall (80, 443)" Write-Host "8. Fazer Backup do IIS e Sites" Write-Host "9. Executar Todas as Etapas (Exceto Backup e DISM)" Write-Host "Q. Sair"
$choice = Read-Host "Escolha uma opção"
switch ($choice) { "1" { Ensure-LocalWebdevUser -User $LocalUser -Pass $LocalUserPassword -force:$Force } "2" { Ensure-IIS -SiteName $SiteName } "3" { Configure-WAS -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -LocalUserPassword $LocalUserPassword -Force:$Force -CopyDllsToBin:$CopyDllsToBin -InstallPathC $InstallPathC -InstallPathD $InstallPathD } "4" { Create-VirtualApplication -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -Force:$Force -InstallPathC $InstallPathC -InstallPathD $InstallPathD } "5" { Run-Diagnostics -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -InstallPathC $InstallPathC -InstallPathD $InstallPathD } "6" { Fix-Dism } "7" { Open-FirewallPorts } "8" { Backup-IISConfiguration -SiteName $SiteName } "9" { Ensure-LocalWebdevUser -User $LocalUser -Pass $LocalUserPassword -force:$Force Ensure-IIS -SiteName $SiteName Configure-WAS -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -LocalUserPassword $LocalUserPassword -Force:$Force -CopyDllsToBin:$CopyDllsToBin -InstallPathC $InstallPathC -InstallPathD $InstallPathD Create-VirtualApplication -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -Force:$Force -InstallPathC $InstallPathC -InstallPathD $InstallPathD Run-Diagnostics -WebdevVersion $WebdevVersion -ProjectName $ProjectName -SiteName $SiteName -LocalUser $LocalUser -InstallPathC $InstallPathC -InstallPathD $InstallPathD Open-FirewallPorts } "q" { return } default { Write-Host "Opção inválida." -ForegroundColor Red } } } }
# Executa a função principal Main
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 22,septiembre 2025 - 07:31 |
O script PowerShell `Setup-WAS-IIS_v2.ps1` é uma ferramenta abrangente para configurar, validar e manter o Microsoft Internet Information Server (IIS) e o WebDev Application Server (WAS) da PC SOFT. Ele simplifica a garantia de que seu ambiente esteja corretamente configurado para hospedar sites, webservices e websockets desenvolvidos com as ferramentas PC SOFT. Além disso, oferece funcionalidades essenciais de backup e verificações de permissão.
As principais funcionalidades incluem:
• Criação de Usuário Local Dedicado: Cria um usuário local com as permissões necessárias para o WAS. • Instalação e Configuração do IIS: Garante que os recursos do IIS necessários para o WebDev/WAS estejam instalados. • Configuração do WAS: Realiza a configuração específica do WAS, incluindo Application Pool, Handlers e permissões NTFS. • Criação de Aplicação Virtual: Cria uma aplicação virtual no IIS para projetos WebDev. • Diagnóstico Completo: Realiza verificações para garantir o funcionamento correto do IIS e WAS. • Correção de Acesso ao DISM: Tenta corrigir problemas de acesso ao serviço DISM. • Abertura de Portas do Firewall: Configura o firewall para permitir tráfego nas portas 80 e 443. • Backup do IIS e Sites: Realiza backup da configuração do IIS e dos arquivos `web.config` dos sites.
Você pode encontrar mais detalhes no arquivo `README.md` que foi anexado anteriormente, junto com o script.
E no http://repository.windev.com onde coloquei os fontes:
https://repository.windev.com/resource.awp…
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/Mensaje modificado, 22,septiembre 2025 - 07:43 |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 05:11 |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 22:13 |
Manual de uso
Organograma do Projeto
WAS_Orchestrator_Kit_v5.0/ └── Bundle/ ├── Scripts/ │ ├── WAS-Orchestrator.ps1 │ ├── Metrics-Exporter.ps1 │ ├── Metrics-Exporter-Service.ps1 │ ├── Metrics-Exporter-Wrapper.ps1 │ ├── DR-Plan.ps1 │ ├── Sign-All.ps1 │ └── Validate-Install.ps1 ├── Profiles/ │ └── DR-Plan.yaml ├── Installer/ │ └── Build-Installer-EXE.cmd └── Docs/ └── WAS-Orchestrator-Addendum-v5.0.txt
Detalhes
• WAS-Orchestrator.ps1 → menu principal (IIS/WAS, SafeTest, Hardening, Diagnósticos). • Metrics-Exporter.ps1 → exporter Prometheus/Grafana com gauges reais de IIS/WAS/TLS. • Metrics-Exporter-Service.ps1 → instala/gerencia exporter como serviço Windows. • Metrics-Exporter-Wrapper.ps1 → watchdog que reinicia exporter. • DR-Plan.ps1 → executor real do YAML (usando YamlDotNet ou ConvertFrom-Yaml PowerShell 7). • Sign-All.ps1 → assinatura Authenticode completa (com parâmetros de certificado). • Validate-Install.ps1 → validação de integridade com SHA256. • DR-Plan.yaml → perfil de contingência atualizado. • Build-Installer-EXE.cmd → build automático com IExpress + catálogo. • WAS-Orchestrator-Addendum-v5.0.txt → manual revisado.
Link download
🟣 Script 1: WAS-Orchestrator.ps1 (v5.0)
Este é o menu principal do orquestrador. Agora inclui: • Start/Stop IIS e WAS • Safe Test Mode (abre portas + desliga firewall + reverte) • Hardening Max • Diagnósticos detalhados (IIS, WAS, Sites) • Validação de integridade (chama Validate-Install.ps1) • Execução de DR-Plan (DR-Plan.ps1) • Exporter status (verifica serviço do Metrics-Exporter)
#requires -RunAsAdministrator <# .SYNOPSIS WAS Orchestrator v5.0 .DESCRIPTION Orquestrador completo para IIS (Msiis) e Webdev Application Server (W.A.S.). Inclui instalação, hardening, diagnósticos, DR-Plan, Exporter Prometheus e SafeTest. .AUTHOR Adriano José Boller - PC SOFT Brasil .VERSION 5.0 #>
function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Cyan } function Write-Success { Write-Host "[ OK ] $args" -ForegroundColor Green } function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow } function Write-ErrorX { Write-Host "[FAIL] $args" -ForegroundColor Red }
# ============================== # IIS Service Ops # ============================== function Start-IIS { try { Start-Service W3SVC; Write-Success "IIS iniciado." } catch { Write-ErrorX $_ } } function Stop-IIS { try { Stop-Service W3SVC -Force; Write-Success "IIS parado." } catch { Write-ErrorX $_ } }
# ============================== # WAS Service Ops # ============================== function Start-WAS { try { Get-Service | Where-Object { $_.Name -like "WD*AWP" } | Start-Service; Write-Success "WAS iniciado." } catch { Write-ErrorX $_ } } function Stop-WAS { try { Get-Service | Where-Object { $_.Name -like "WD*AWP" } | Stop-Service -Force; Write-Success "WAS parado." } catch { Write-ErrorX $_ } }
# ============================== # Safe Test Mode # ============================== function Invoke-SafeTest { Write-Warn "Ativando SAFE TEST..." $ports = @(4900,4901,4902,4910,4920) foreach ($p in $ports) { netsh advfirewall firewall add rule name="WAS SafeTest $p" dir=in action=allow protocol=TCP localport=$p netsh advfirewall firewall add rule name="WAS SafeTest $p" dir=out action=allow protocol=TCP localport=$p } netsh advfirewall set allprofiles state off Pause Write-Info "Revertendo SafeTest..." netsh advfirewall set allprofiles state on foreach ($p in $ports) { netsh advfirewall firewall delete rule name="WAS SafeTest $p" } Write-Success "SafeTest revertido." }
# ============================== # Hardening Max # ============================== function Invoke-HardeningMax { Import-Module WebAdministration $pool = "WebdevAppPool" if (-not (Get-WebAppPoolState -Name $pool -ErrorAction SilentlyContinue)) { New-WebAppPool -Name $pool } Set-ItemProperty "IIS:\AppPools\$pool" -Name managedRuntimeVersion -Value "" Set-ItemProperty "IIS:\AppPools\$pool" -Name processModel.idleTimeout -Value ([TimeSpan]::Zero) Set-ItemProperty "IIS:\AppPools\$pool" -Name recycling.periodicRestart.time -Value ([TimeSpan]::Zero) Set-ItemProperty "IIS:\AppPools\$pool" -Name startMode -Value "AlwaysRunning" Set-ItemProperty "IIS:\AppPools\$pool" -Name autoStart -Value $true Write-Success "Hardening aplicado no pool $pool." }
# ============================== # Diagnósticos # ============================== function Invoke-WASDiagnostics { Write-Info "Executando diagnósticos..." $iis = Get-Service W3SVC -ErrorAction SilentlyContinue if ($iis.Status -eq 'Running') { Write-Success "IIS ativo" } else { Write-ErrorX "IIS parado" } $was = Get-Service | Where-Object { $_.Name -like "WD*AWP" } foreach ($svc in $was) { if ($svc.Status -eq 'Running') { Write-Success "WAS $($svc.Name) ativo" } else { Write-ErrorX "WAS $($svc.Name) parado" } } Get-Website | ForEach-Object { Write-Info "Site: $($_.Name) - Estado: $($_.State)" } }
# ============================== # Integração com outros módulos # ============================== function Run-DRPlan { & "$PSScriptRoot\DR-Plan.ps1" } function Run-ValidateInstall { & "$PSScriptRoot\Validate-Install.ps1" } function Check-Exporter { Get-Service | Where-Object { $_.Name -eq "WASExporter" } | ForEach-Object { Write-Info "Exporter: $($_.Status)" } }
# ============================== # Menu Principal # ============================== function Show-MainMenu { Clear-Host Write-Host "===============================" -ForegroundColor Magenta Write-Host " WAS Orchestrator v5.0" -ForegroundColor Magenta Write-Host "===============================" -ForegroundColor Magenta Write-Host "1. Iniciar IIS" Write-Host "2. Parar IIS" Write-Host "3. Iniciar WAS" Write-Host "4. Parar WAS" Write-Host "5. Safe Test Mode" Write-Host "6. Hardening Max" Write-Host "7. Rodar Diagnósticos" Write-Host "8. Executar DR-Plan" Write-Host "9. Validar Integridade" Write-Host "10. Status do Exporter" Write-Host "0. Sair" Write-Host "===============================" $choice = Read-Host "Escolha uma opção" switch ($choice) { "1" { Start-IIS } "2" { Stop-IIS } "3" { Start-WAS } "4" { Stop-WAS } "5" { Invoke-SafeTest } "6" { Invoke-HardeningMax } "7" { Invoke-WASDiagnostics } "8" { Run-DRPlan } "9" { Run-ValidateInstall } "10" { Check-Exporter } "0" { exit } default { Write-Warn "Opção inválida" } } Pause Show-MainMenu }
Show-MainMenu
🟢 Script 2: Metrics-Exporter.ps1 (v5.0)
Este script roda um servidor HTTP local que expõe métricas no formato Prometheus/Grafana, incluindo: • Status de IIS e WAS • Número de sites/pools ativos • Métricas TLS (TLS 1.0–1.3 habilitados/desabilitados) • Quantidade de bindings HTTPS • Serviços críticos rodando
#requires -RunAsAdministrator <# .SYNOPSIS Metrics Exporter v5.0 .DESCRIPTION Exporta métricas DO IIS/WAS/TLS No formato Prometheus. Endpoint: http: #>
Add-Type -AssemblyName System.Net.HttpListener
$listener = new-object System.Net.HttpListener $prefix = "http://+:4900/metrics/" $listener.Prefixes.Add($prefix) $listener.Start() Write-Host "[INFO] Exporter iniciado em $prefix" -ForegroundColor Cyan
FUNCTION Get-Metrics { $metrics = @()
# IIS try { $iis = Get-Service W3SVC -ErrorAction STOP $metrics += "was_iis_status " + $(IF ($iis.Status -eq 'Running') {1}ELSE{0}) } catch { $metrics += "was_iis_status 0" }
# WAS try { $was = Get-Service | WHERE-object { $_.Name -like "WD*AWP" } foreach ($svc IN $was) { $metrics += "was_service_status{name=`"$($svc.Name)`"} " + $(IF ($svc.Status -eq 'Running'){1}ELSE{0}) } } catch { $metrics += "was_service_status{name=`"unknown`"} 0" }
# Sites try { Import-Module WebAdministration $sites = Get-Website $metrics += "was_sites_total " + $sites.Count foreach ($s IN $sites) { $val = IF ($s.State -eq "Started") {1}ELSE{0} $metrics += "was_site_status{name=`"$($s.Name)`"} $val" } } catch { $metrics += "was_sites_total 0" }
# TLS Checks (Schannel) $tls10 = (Get-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS 1.0\\Server" -ErrorAction SilentlyContinue)."Enabled" $tls11 = (Get-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS 1.1\\Server" -ErrorAction SilentlyContinue)."Enabled" $tls12 = (Get-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS 1.2\\Server" -ErrorAction SilentlyContinue)."Enabled" $tls13 = (Get-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\SCHANNEL\\Protocols\\TLS 1.3\\Server" -ErrorAction SilentlyContinue)."Enabled"
$metrics += "was_tls10_enabled " + $(IF ($tls10 -eq 1){1}ELSE{0}) $metrics += "was_tls11_enabled " + $(IF ($tls11 -eq 1){1}ELSE{0}) $metrics += "was_tls12_enabled " + $(IF ($tls12 -eq 1){1}ELSE{0}) $metrics += "was_tls13_enabled " + $(IF ($tls13 -eq 1){1}ELSE{0})
# HTTPS Bindings try { $httpsBindings = Get-WebBinding | WHERE-object {$_.protocol -eq "https"} $metrics += "was_https_bindings_total " + $httpsBindings.Count } catch { $metrics += "was_https_bindings_total 0" }
RETURN ($metrics -join "`n") }
# LOOP HTTP WHILE ($listener.IsListening) { $context = $listener.GetContext() $response = $context.Response $buffer = [System.Text.Encoding]::UTF8.GetBytes((Get-Metrics)) $response.ContentLength64 = $buffer.Length $response.ContentType = "text/plain" $response.OutputStream.Write($buffer, 0, $buffer.Length) $response.Close() }
🔍 Como usar 1. Salve como Metrics-Exporter.ps1 em Bundle\Scripts. 2. Execute com:
.\Metrics-Exporter.ps1
3. Acesse em navegador ou Prometheus:
http://localhost:4900/metrics
📊 Saída típica:
was_iis_status 1 was_service_status{name="WDAWP180"} 1 was_sites_total 3 was_site_status{name="Portal"} 1 was_tls12_enabled 1 was_tls13_enabled 1 was_https_bindings_total 2
🟡 Script 3: Metrics-Exporter-Service.ps1 (v5.0)
Este script permite instalar, iniciar, parar e remover o Metrics-Exporter como serviço do Windows. Assim, ele roda em background sem precisar abrir console manualmente.
#requires -RunAsAdministrator <# .SYNOPSIS Metrics Exporter Service v5.0 .DESCRIPTION Instala/gerencia o Metrics-Exporter.ps1 como serviço Windows. .PARAMETER Action Install | Uninstall | Start | STOP | Restart | Status #>
param( [Parameter(Mandatory=$true)] [ValidateSet("Install","Uninstall","Start","Stop","Restart","Status")] [string]$Action )
$ServiceName = "WASExporter" $DisplayName = "WAS Metrics Exporter Service" $ExporterScript = Join-Path $PSScriptRoot "Metrics-Exporter.ps1" $NssmPath = "C:\nssm\nssm.exe" # precisa DO NSSM (Non-Sucking Service Manager)
FUNCTION Install-Service { IF (-not (Test-Path $NssmPath)) { Write-Host "[FAIL] NSSM não encontrado em $NssmPath" -ForegroundColor Red RETURN } & $NssmPath install $ServiceName "powershell.exe" "-ExecutionPolicy Bypass -File `"$ExporterScript`"" & $NssmPath set $ServiceName DisplayName "$DisplayName" & $NssmPath set $ServiceName Start SERVICE_AUTO_START Start-Service $ServiceName Write-Host "[OK] Serviço $DisplayName instalado e iniciado." -ForegroundColor Green }
FUNCTION Uninstall-Service { IF (Get-Service -Name $ServiceName -ErrorAction SilentlyContinue) { STOP-Service $ServiceName -Force & $NssmPath remove $ServiceName Confirm Write-Host "[OK] Serviço $DisplayName removido." -ForegroundColor Green } ELSE { Write-Host "[WARN] Serviço não encontrado." -ForegroundColor Yellow } }
SWITCH ($Action) { "Install" { Install-Service } "Uninstall"{ Uninstall-Service } "Start" { Start-Service $ServiceName; Write-Host "[OK] Serviço iniciado." -ForegroundColor Green } "Stop" { STOP-Service $ServiceName -Force; Write-Host "[OK] Serviço parado." -ForegroundColor Green } "Restart" { Restart-Service $ServiceName; Write-Host "[OK] Serviço reiniciado." -ForegroundColor Green } "Status" { Get-Service $ServiceName } }
🔍 Como usar 1. Baixe e instale o NSSM (Non-Sucking Service Manager): https://nssm.cc/download Copie nssm.exe para C:\nssm\nssm.exe. 2. Para instalar o Exporter como serviço:
.\Metrics-Exporter-Service.ps1 -Action Install
3. Ver status:
.\Metrics-Exporter-Service.ps1 -Action Status
4. Parar:
.\Metrics-Exporter-Service.ps1 -Action STOP
5. Remover:
.\Metrics-Exporter-Service.ps1 -Action Uninstall
🔵 Script 4: Metrics-Exporter-Wrapper.ps1 (v5.0)
Este script é um watchdog: mantém o Metrics-Exporter.ps1 sempre ativo. Se o processo cair ou travar, ele reinicia automaticamente. Pode ser rodado no Task Scheduler ou integrado ao serviço.
O que faz: Watchdog simples para manter o Metrics-Exporter.ps1 sempre de pé. • Garante instância única (mutex). • Abre a porta 4900 no firewall (se não existir regra). • Sobe o Exporter, monitora e reinicia em caso de falha. • Registra logs em C:\WAS\Logs\metrics-exporter-wrapper.log.
Use este wrapper quando rodar o exporter via Agendador ou serviço “cru” (sem NSSM). Se usar o serviço com NSSM (Script 3), o NSSM já faz parte desse papel — mas você pode manter o wrapper por uma camada extra de resiliência.
#requires -RunAsAdministrator <# .SYNOPSIS Metrics Exporter Wrapper v5.0 .DESCRIPTION Watchdog para o Metrics-Exporter.ps1: garante 1 instância, abre firewall, reinicia em caso de falha e grava logs. .PARAMETER ExporterPath Caminho DO Metrics-Exporter.ps1 (padrão: ao lado deste script). .PARAMETER Port Porta HTTP DO exporter (padrão 4900). .PARAMETER LogPath Caminho DO Log DO wrapper (padrão: C:\WAS\Logs\metrics-exporter-wrapper.log). .PARAMETER RestartDelaySec Tempo de espera antes de reiniciar após queda (padrão: 3s). #>
param( [string]$ExporterPath = (Join-Path $PSScriptRoot "Metrics-Exporter.ps1"), [int]$Port = 4900, [string]$LogPath = "C:\WAS\Logs\metrics-exporter-wrapper.log", [int]$RestartDelaySec = 3 )
# FUNCTION Write-Log($msg,[string]$level="INFO"){ $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "[{0}] [{1}] {2}" -f $ts,$level,$msg Write-Host $line $dir = Split-Path $LogPath -Parent IF (-not (Test-Path $dir)) { new-Item -ItemType Directory -Path $dir -Force | Out-Null } Add-Content -Path $LogPath -Value $line }
# $mutexName = "Global\WAS_MetricsExporter_Wrapper" $created = $false try { $mutex = new-object System.Threading.Mutex($true, $mutexName, [ref]$created) IF (-not $created) { Write-Log "Já existe uma instância do wrapper em execução (mutex=$mutexName)." "WARN" exit 0 } } catch { Write-Log "Falha criando mutex único: $($_.Exception.Message)" "FAIL" exit 1 }
# IF (-not (Test-Path $ExporterPath)) { Write-Log "Metrics-Exporter não encontrado em: $ExporterPath" "FAIL" exit 1 }
# try { $ruleName = "WAS Metrics Exporter (TCP $Port)" $existing = netsh advfirewall firewall show rule name="$ruleName" | Out-string IF ($existing -notmatch "Rule Name") { Write-Log "Criando regra de firewall para porta $Port (in/out)." netsh advfirewall firewall Add rule name="$ruleName" dir=IN action=allow protocol=TCP localport=$Port | Out-Null netsh advfirewall firewall Add rule name="$ruleName" dir=out action=allow protocol=TCP localport=$Port | Out-Null } ELSE { Write-Log "Regra de firewall já existe: $ruleName" } } catch { Write-Log "Falha ao configurar firewall: $($_.Exception.Message)" "WARN" }
# Write-Log "Iniciando watchdog. Exporter: $ExporterPath | Porta: $Port | Log: $LogPath" WHILE ($true) { try { $psi = new-object System.Diagnostics.ProcessStartInfo $psi.FileName = "powershell.exe" $psi.Arguments = "-NoProfile -ExecutionPolicy Bypass -File `"$ExporterPath`"" $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.UseShellExecute = $false $psi.CreateNoWindow = $true
$proc = new-object System.Diagnostics.Process $proc.StartInfo = $psi [void]$proc.Start() Write-Log "Exporter iniciado. PID=$($proc.Id)"
# leitura não bloqueante dos streams $proc.BeginOutputReadLine() $proc.BeginErrorReadLine()
# aguarda saída $proc.WaitForExit() $code = $proc.ExitCode Write-Log "Exporter encerrou. ExitCode=$code" "WARN" } catch { Write-Log "Erro ao executar exporter: $($_.Exception.Message)" "FAIL" }
Write-Log "Aguardando $RestartDelaySec s para reinício..." Start-Sleep -Seconds $RestartDelaySec }
💡 Como usar
C:\WAS\Bundle\Scripts\Metrics-Exporter-Wrapper.ps1 -ExporterPath "C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1" -Port 4900
• Agendador: crie uma tarefa que rode o wrapper no logon. • Como serviço (sem NSSM): você pode usar sc.exe create apontando para este wrapper, mas recomendo o NSSM do Script 3 para uma gestão mais limpa.
C:\WAS\Bundle\Scripts\Metrics-Exporter-Wrapper.ps1 -ExporterPath "C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1" -Port 4901
🧰 Script 5: DR-Plan.ps1 (v5.0)
O que faz: Lê o arquivo Profiles\DR-Plan.yaml (ou um caminho informado), interpreta o plano de contingência e aplica: • criticalSite: site prioritário para manter de pé; • stopOthers: pausa os demais application pools/sites não essenciais; • prioritize443: libera e reserva os bindings HTTPS (443) só para o site crítico; • order: ordem de start adicional; • banner: publica aviso de contingência no Portal de Auditoria.
Inclui: • Fallback para parser YAML básico quando ConvertFrom-Yaml (PS7+) não estiver disponível; • Snapshot de estado antes da mudança (JSON) e Rollback opcional; • Logs em C:\WAS\Logs\dr-plan.log.
#requires -RunAsAdministrator <# .SYNOPSIS DR-Plan v5.0 – Executor de plano de contingência (YAML) .DESCRIPTION Aplica um plano de DR a partir de Profiles\DR-Plan.yaml (ou arquivo informado). - Mantém site crítico ativo - Opcionalmente para outros pools/sites - Pode priorizar a porta 443 para o site crítico (removendo https dos demais) - Sobe sites em ordem declarada - Publica banner de contingência no portal - Snapshot e rollback .PARAMETER PlanFile Caminho do YAML (padrão: C:\WAS\Bundle\Profiles\DR-Plan.yaml) .PARAMETER Rollback Reverte para o snapshot mais recente (em C:\WAS\Reports\DR-Snapshots\) .PARAMETER PortalPath Caminho do portal para publicar banner (padrão: C:\inetpub\WAS-AuditPortal) .PARAMETER LogPath Caminho do log (padrão: C:\WAS\Logs\dr-plan.log) #> param( [string]$PlanFile = "C:\WAS\Bundle\Profiles\DR-Plan.yaml", [switch]$Rollback, [string]$PortalPath = "C:\inetpub\WAS-AuditPortal", [string]$LogPath = "C:\WAS\Logs\dr-plan.log" ) $ErrorActionPreference = "Stop" function Write-Log($msg,[string]$level="INFO"){ $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "[{0}] [{1}] {2}" -f $ts,$level,$msg Write-Host $line $dir = Split-Path $LogPath -Parent if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } Add-Content -Path $LogPath -Value $line } function Ensure-ModuleWebAdmin { if (-not (Get-Module -ListAvailable -Name WebAdministration)) { throw "Módulo WebAdministration não disponível. Instale o IIS Management Scripts and Tools." } Import-Module WebAdministration -ErrorAction Stop } function Parse-YamlBasic([string]$path){ # Parser mínimo para as chaves suportadas no plano # Suporta: # criticalSite: Nome # stopOthers: true|false # prioritize443: true|false # order: (lista com '- item') # banner: "texto" $result = @{ criticalSite = "" stopOthers = $false prioritize443 = $false order = @() banner = "" } $lines = Get-Content $path $inOrder = $false foreach($ln in $lines){ $t = $ln.Trim() if ($t -match "^\s*#") { continue } if ($t -match "^\s*$") { continue } if ($t -match "^\s*order\s*:\s*$"){ $inOrder = $true; continue } if ($inOrder -and $t -match "^\s*-\s*(.+)$"){ $result.order += ($t -replace "^\s*-\s*","").Trim(); continue } if ($t -match "^\s*[A-Za-z]+:") { $inOrder = $false } if ($t -match "^\s*criticalSite\s*:\s*(.+)$"){ $result.criticalSite = $matches[1].Trim('"'); continue } if ($t -match "^\s*stopOthers\s*:\s*(.+)$"){ $result.stopOthers = [bool]::Parse($matches[1]); continue } if ($t -match "^\s*prioritize443\s*:\s*(.+)$"){ $result.prioritize443 = [bool]::Parse($matches[1]); continue } if ($t -match "^\s*banner\s*:\s*\"(.+)\"$"){ $result.banner = $matches[1]; continue } } return $result } function Load-Plan([string]$file){ if (-not (Test-Path $file)) { throw "Plano não encontrado: $file" } try { if (Get-Command ConvertFrom-Yaml -ErrorAction SilentlyContinue) { $obj = Get-Content $file -Raw | ConvertFrom-Yaml # normaliza para hashtable $plan = @{ criticalSite = "$($obj.criticalSite)" stopOthers = [bool]$obj.stopOthers prioritize443 = [bool]$obj.prioritize443 order = @() banner = "$($obj.banner)" } if ($obj.order) { $plan.order = @($obj.order | ForEach-Object { "$_" }) } return $plan } else { Write-Log "ConvertFrom-Yaml não disponível; usando parser básico." "WARN" return (Parse-YamlBasic $file) } } catch { throw "Falha ao carregar plano: $($_.Exception.Message)" } } function Snapshot-State { Ensure-ModuleWebAdmin $snap = @{ timestamp = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss") sites = @() appPools = @() httpsBinds = @() } $sites = Get-Website foreach($s in $sites){ $snap.sites += @{ name = $s.Name state = $s.State } } $pools = Get-WebAppPoolState foreach($p in $pools){ $snap.appPools += @{ name = $p.ItemName state = $p.Value } } foreach($s in $sites){ $b = Get-WebBinding -Name $s.Name -Protocol https -ErrorAction SilentlyContinue if ($b) { foreach($bi in @($b)){ $snap.httpsBinds += @{ site = $s.Name bindingInformation = $bi.bindingInformation } } } } $outDir = "C:\WAS\Reports\DR-Snapshots" if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null } $file = Join-Path $outDir ("snapshot-{0}.json" -f (Get-Date -Format "yyyyMMdd-HHmmss")) $snap | ConvertTo-Json -Depth 5 | Out-File -FilePath $file -Encoding UTF8 Write-Log "Snapshot salvo: $file" return $file } function Find-LastSnapshot { $dir = "C:\WAS\Reports\DR-Snapshots" if (-not (Test-Path $dir)) { return $null } $files = Get-ChildItem $dir -Filter "snapshot-*.json" | Sort-Object LastWriteTime -Descending if ($files.Count -gt 0) { return $files[0].FullName } else { return $null } } function Apply-Rollback([string]$snapshotFile){ Ensure-ModuleWebAdmin if (-not (Test-Path $snapshotFile)) { throw "Snapshot não encontrado: $snapshotFile" } $snap = Get-Content $snapshotFile -Raw | ConvertFrom-Json # 1) Restaurar bindings https conforme snapshot (remove extras) $currentSites = Get-Website foreach($s in $currentSites){ # remove todos https atuais Get-WebBinding -Name $s.Name -Protocol https -ErrorAction SilentlyContinue | ForEach-Object { Remove-WebBinding -Name $s.Name -BindingInformation $_.bindingInformation -Protocol https -ErrorAction SilentlyContinue } } # recria os do snapshot foreach($hb in $snap.httpsBinds){ try { New-WebBinding -Name $hb.site -Protocol https -BindingInformation $hb.bindingInformation -ErrorAction SilentlyContinue } catch { Write-Log "Falha ao restaurar binding $($hb.site) :: $($hb.bindingInformation) -> $($_.Exception.Message)" "WARN" } } # 2) Restaurar estados de sites foreach($s in $snap.sites){ try { if ($s.state -eq "Started") { Start-Website -Name $s.name -ErrorAction SilentlyContinue } else { Stop-Website -Name $s.name -ErrorAction SilentlyContinue } } catch { Write-Log "Falha ao restaurar site $($s.name): $($_.Exception.Message)" "WARN" } } # 3) Restaurar estados de pools foreach($p in $snap.appPools){ try { if ($p.state -eq "Started") { Start-WebAppPool -Name $p.name -ErrorAction SilentlyContinue } else { Stop-WebAppPool -Name $p.name -ErrorAction SilentlyContinue } } catch { Write-Log "Falha ao restaurar pool $($p.name): $($_.Exception.Message)" "WARN" } } Write-Log "Rollback concluído a partir de: $snapshotFile" "OK" } function Publish-Banner([string]$portal,[string]$message){ if (-not $message -or $message.Trim().Length -eq 0) { return } try { if (-not (Test-Path $portal)) { New-Item -ItemType Directory -Path $portal -Force | Out-Null } $bannerPath = Join-Path $portal "dr-banner.html" $html = @" DR MODE: $message — $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
"@ $html | Out-File -FilePath $bannerPath -Encoding UTF8 Write-Log "Banner publicado em: $bannerPath" } catch { Write-Log "Falha ao publicar banner: $($_.Exception.Message)" "WARN" } } function Apply-Plan([hashtable]$plan){ Ensure-ModuleWebAdmin if (-not $plan.criticalSite -or $plan.criticalSite.Trim().Length -eq 0) { throw "criticalSite não definido no plano." } $snapshotFile = Snapshot-State # 1) Start site crítico + seu pool $critical = $plan.criticalSite Write-Log "Iniciando site crítico: $critical" $site = Get-Website -Name $critical -ErrorAction SilentlyContinue if (-not $site) { throw "Site crítico não encontrado no IIS: $critical" } $poolName = (Get-ItemProperty "IIS:\Sites\$critical").applicationPool if ($poolName) { try { Start-WebAppPool -Name $poolName -ErrorAction SilentlyContinue } catch {} } try { Start-Website -Name $critical -ErrorAction SilentlyContinue } catch {} # 2) Stop outros pools/sites (opcional) if ($plan.stopOthers) { Write-Log "Parando pools/sites não críticos." # pools Get-WebAppPoolState | Where-Object { $_.ItemName -ne $poolName } | ForEach-Object { try { Stop-WebAppPool -Name $_.ItemName -ErrorAction SilentlyContinue } catch {} } # sites Get-Website | Where-Object { $_.Name -ne $critical } | ForEach-Object { try { Stop-Website -Name $_.Name -ErrorAction SilentlyContinue } catch {} } } # 3) Priorizar 443 para site crítico (opcional) if ($plan.prioritize443) { Write-Log "Priorizando 443 para $critical (removendo https dos demais)." $allSites = Get-Website foreach ($s in $allSites) { if ($s.Name -ne $critical) { Get-WebBinding -Name $s.Name -Protocol https -ErrorAction SilentlyContinue | ForEach-Object { try { Remove-WebBinding -Name $s.Name -BindingInformation $_.bindingInformation -Protocol https -ErrorAction SilentlyContinue } catch {} } } } } # 4) Start ordem adicional if ($plan.order -and $plan.order.Count -gt 0) { foreach($name in $plan.order){ if ($name -eq $critical) { continue } try { Start-Website -Name $name -ErrorAction SilentlyContinue Write-Log "Site iniciado (order): $name" } catch { Write-Log "Falha ao iniciar site da ordem: $name -> $($_.Exception.Message)" "WARN" } } } # 5) Publicar banner Publish-Banner -portal $PortalPath -message $plan.banner Write-Log "Plano de DR aplicado com sucesso. Snapshot: $snapshotFile" "OK" return $snapshotFile } # ======================= # Main # ======================= try { if ($Rollback) { $last = Find-LastSnapshot if (-not $last) { Write-Log "Nenhum snapshot encontrado para rollback." "WARN" exit 1 } Apply-Rollback -snapshotFile $last Write-Log "Rollback finalizado." exit 0 } $plan = Load-Plan -file $PlanFile Write-Log "Plano carregado. criticalSite=$($plan.criticalSite); stopOthers=$($plan.stopOthers); prioritize443=$($plan.prioritize443); order=$(@($plan.order) -join ', ')" $snap = Apply-Plan -plan $plan Write-Log "DR-Plan concluído. Snapshot: $snap" exit 0 } catch { Write-Log "ERRO: $($_.Exception.Message)" "FAIL" exit 2 }
🔧 Como usar 1. Aplicar o plano padrão:
C:\WAS\Bundle\Scripts\DR-Plan.ps1
2. Usar arquivo YAML customizado:
C:\WAS\Bundle\Scripts\DR-Plan.ps1 -PlanFile "D:\Planos\DR-Portal.yaml"
3. Publicar em outro Portal (para o banner):
C:\WAS\Bundle\Scripts\DR-Plan.ps1 -PortalPath "C:\inetpub\MeuPortalDR"
4. Rollback usando o último snapshot:
C:\WAS\Bundle\Scripts\DR-Plan.ps1 -Rollback
🧾 Script 6: Sign-All.ps1 (v5.0)
O que faz: Assina (Authenticode) todos os scripts do bundle com um certificado de Code Signing disponível no Windows Certificate Store. • Seleção de certificado por Thumbprint ou Subject (fallback: mais novo válido). • Suporte a Timestamp Server. • Modos verificar apenas, reassinar, what-if/dry-run. • Filtros de arquivos (include/exclude), recurse, relatório CSV/LOG. • Retorna exit code 0 se tudo OK, 2 se algum arquivo falhou, 1 se nenhum certificado elegível.
Requisitos: PowerShell 5.1+ (ou 7+) e um certificado de Code Signing no LocalMachine\My ou CurrentUser\My.
#requires -RunAsAdministrator <# .SYNOPSIS Assina (Authenticode) arquivos do WAS Orchestrator Kit. .DESCRIPTION Localiza um certificado de Code Signing no Windows Store e aplica assinatura Authenticode a todos os arquivos alvo (ex.: .ps1, .psm1) do bundle. Suporta seleção por Thumbprint/Subject, Timestamp Server, verificação, re-assinatura e geração de relatórios (CSV/LOG). .PARAMETER Path Raiz onde procurar arquivos (padrão: C:\WAS\Bundle). .PARAMETER Include Padrões de arquivo a incluir (padrão: *.ps1, *.psm1). .PARAMETER Exclude Padrões de arquivo a excluir (opcional). .PARAMETER Recurse Procura recursivamente (padrão: ligado). .PARAMETER StoreLocation LocalMachine ou CurrentUser (padrão: LocalMachine). .PARAMETER StoreName Nome do store (padrão: My). .PARAMETER Thumbprint Thumbprint do certificado (opcional). .PARAMETER SubjectLike Filtro de Subject (ex.: "CN=MinhaEmpresa") (opcional). .PARAMETER TimestampServer URL de servidor de timestamp (opcional). .PARAMETER VerifyOnly Apenas verifica assinaturas (não assina). .PARAMETER Resign Força re-assinatura (mesmo se já assinado). .PARAMETER WhatIf Simula (não altera nada). .PARAMETER ReportCsv Caminho do relatório CSV (padrão: C:\WAS\Reports\sign-report.csv). .PARAMETER LogPath Caminho do log (padrão: C:\WAS\Logs\sign-all.log). .EXAMPLE .\Sign-All.ps1 .EXAMPLE .\Sign-All.ps1 -Thumbprint "A1B2C3..." -TimestampServer http://timestamp.digicert.com .EXAMPLE .\Sign-All.ps1 -SubjectLike "CN=MinhaEmpresa" -Include *.ps1,*.psm1 -Resign .EXAMPLE .\Sign-All.ps1 -VerifyOnly -ReportCsv D:\Relatorios\sign.csv #>
[CmdletBinding(SupportsShouldProcess)] param( [string]$Path = "C:\WAS\Bundle", [string[]]$Include = @("*.ps1","*.psm1"), [string[]]$Exclude = @(), [switch]$Recurse = $true,
[ValidateSet("LocalMachine","CurrentUser")] [string]$StoreLocation = "LocalMachine", [string]$StoreName = "My", [string]$Thumbprint, [string]$SubjectLike, [string]$TimestampServer,
[switch]$VerifyOnly, [switch]$Resign, [switch]$WhatIf,
[string]$ReportCsv = "C:\WAS\Reports\sign-report.csv", [string]$LogPath = "C:\WAS\Logs\sign-all.log" )
$ErrorActionPreference = "Stop"
# ---------------------------- # Utils # ---------------------------- function Write-Log($msg,[string]$level="INFO"){ $ts = Get-Date -Format "yyyy-MM-dd HH:mm:ss" $line = "[{0}] [{1}] {2}" -f $ts,$level,$msg Write-Host $line $dir = Split-Path $LogPath -Parent if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } Add-Content -Path $LogPath -Value $line } function Ensure-Dirs { foreach($p in @($ReportCsv,$LogPath)){ $d = Split-Path $p -Parent if (-not (Test-Path $d)) { New-Item -ItemType Directory -Path $d -Force | Out-Null } } } function Get-CodeSigningCert { param( [string]$StoreLocation, [string]$StoreName, [string]$Thumbprint, [string]$SubjectLike ) $storePath = "Cert:\{0}\{1}" -f $StoreLocation,$StoreName $cands = Get-ChildItem $storePath -ErrorAction SilentlyContinue | Where-Object { $_.HasPrivateKey -and $_.NotAfter -gt (Get-Date) -and $_.EnhancedKeyUsageList -and ($_.EnhancedKeyUsageList.FriendlyName -contains "Code Signing" -or $_.EnhancedKeyUsageList.ObjectId -contains "1.3.6.1.5.5.7.3.3") }
if ($Thumbprint) { $sel = $cands | Where-Object { $_.Thumbprint -replace "\s","" -ieq ($Thumbprint -replace "\s","") } if ($sel) { return ($sel | Sort-Object NotAfter -Descending | Select-Object -First 1) } } if ($SubjectLike) { $sel = $cands | Where-Object { $_.Subject -like "*$SubjectLike*" -or $_.SubjectName.Name -like "*$SubjectLike*" } if ($sel) { return ($sel | Sort-Object NotAfter -Descending | Select-Object -First 1) } } return ($cands | Sort-Object NotAfter -Descending | Select-Object -First 1) }
# ---------------------------- # Main # ---------------------------- try { Ensure-Dirs Write-Log "=== Sign-All.ps1 start ===" Write-Log "Path=$Path; Include=$(@($Include) -join ','); Exclude=$(@($Exclude) -join ','); Recurse=$Recurse" Write-Log "Store=$StoreLocation\$StoreName; TP=$Thumbprint; SubjectLike=$SubjectLike; TS=$TimestampServer" Write-Log "VerifyOnly=$VerifyOnly; Resign=$Resign; WhatIf=$WhatIf"
if (-not (Test-Path $Path)) { throw "Path não encontrado: $Path" }
# Seleciona certificado (a menos que esteja em VerifyOnly) $cert = $null if (-not $VerifyOnly) { $cert = Get-CodeSigningCert -StoreLocation $StoreLocation -StoreName $StoreName -Thumbprint $Thumbprint -SubjectLike $SubjectLike if (-not $cert) { Write-Log "Nenhum certificado de Code Signing encontrado." "FAIL" exit 1 } Write-Log ("Usando certificado: {0} | Thumbprint={1} | Expira={2}" -f $cert.Subject,$cert.Thumbprint,$cert.NotAfter) }
# Coleta arquivos $files = @() foreach($inc in $Include){ $gciParams = @{ Path=$Path; Filter=$inc; File=$true; ErrorAction="SilentlyContinue" } if ($Recurse) { $gciParams.Recurse = $true } $files += Get-ChildItem @gciParams } if ($Exclude -and $Exclude.Count -gt 0) { foreach($pat in $Exclude){ $files = $files | Where-Object { $_.Name -notlike $pat } } } # Normaliza duplicatas $files = $files | Sort-Object FullName -Unique
if ($files.Count -eq 0) { Write-Log "Nenhum arquivo encontrado para processar em $Path (Include/Exclude aplicados)." "WARN" exit 0 }
# Processa $report = @() $ok=0; $warn=0; $fail=0 foreach($f in $files){ $sig = Get-AuthenticodeSignature -FilePath $f.FullName -ErrorAction SilentlyContinue $statusBefore = $sig.Status.ToString() $shouldSign = $false
if ($VerifyOnly) { Write-Log ("[VERIFY] {0} -> {1}" -f $f.FullName,$statusBefore) } else { if ($Resign) { $shouldSign = $true } else { if ($sig.Status -eq "NotSigned" -or $sig.Status -eq "UnknownError") { $shouldSign = $true } }
if ($shouldSign) { if ($WhatIf) { Write-Log ("[WHATIF] Assinaria: {0}" -f $f.FullName) "WARN" } else { try { if ($TimestampServer) { $res = Set-AuthenticodeSignature -FilePath $f.FullName -Certificate $cert -TimestampServer $TimestampServer -ErrorAction Stop } else { $res = Set-AuthenticodeSignature -FilePath $f.FullName -Certificate $cert -ErrorAction Stop } $statusAfter = $res.Status.ToString() Write-Log ("[SIGNED] {0} -> {1}" -f $f.FullName,$statusAfter) } catch { $statusAfter = "SignError: " + $_.Exception.Message Write-Log ("[FAIL ] {0} -> {1}" -f $f.FullName,$statusAfter) "FAIL" } } } else { $statusAfter = $statusBefore Write-Log ("[SKIP ] {0} -> já assinado ({1})" -f $f.FullName,$statusAfter) } }
if (-not $VerifyOnly -and -not $WhatIf -and $shouldSign) { # pós-checagem $sig2 = Get-AuthenticodeSignature -FilePath $f.FullName -ErrorAction SilentlyContinue $statusAfter = $sig2.Status.ToString() } elseif (-not $shouldSign) { $statusAfter = $statusBefore }
$report += [pscustomobject]@{ File = $f.FullName StatusBefore = $statusBefore Action = $(if ($VerifyOnly) {"Verify"} elseif ($WhatIf) {"WhatIf"} elseif ($shouldSign) {"Sign"} else {"Skip"}) StatusAfter = $statusAfter CertThumb = $(if ($cert) {$cert.Thumbprint} else {""}) CertSubject = $(if ($cert) {$cert.Subject} else {""}) }
# Contadores if ($VerifyOnly) { if ($statusBefore -eq "Valid") { $ok++ } else { $warn++ } } else { if ($shouldSign -and -not $WhatIf) { if ($statusAfter -eq "Valid") { $ok++ } else { $fail++ } } else { # skip/whatif contam como warn se não valid if ($statusAfter -eq "Valid") { $ok++ } else { $warn++ } } } }
# Salva CSV $repDir = Split-Path $ReportCsv -Parent if (-not (Test-Path $repDir)) { New-Item -ItemType Directory -Path $repDir -Force | Out-Null } $report | Export-Csv -Path $ReportCsv -NoTypeInformation -Encoding UTF8
Write-Log ("Resumo: OK={0} WARN={1} FAIL={2} | CSV: {3}" -f $ok,$warn,$fail,$ReportCsv) if ($fail -gt 0) { exit 2 } else { exit 0 } } catch { Write-Log "ERRO: $($_.Exception.Message)" "FAIL" exit 2 } finally { Write-Log "=== Sign-All.ps1 end ===" }
🧪 Exemplos práticos 1. Assinar tudo no bundle com o certificado mais recente de Code Signing do LocalMachine\My:
C:\WAS\Bundle\Scripts\Sign-ALL.ps1
2. Assinar usando Thumbprint específico + timestamp:
C:\WAS\Bundle\Scripts\Sign-ALL.ps1 ` -Thumbprint "?A1B2C3D4E5F6..." ` -TimestampServer "http://timestamp.digicert.com"
3. Verificar assinaturas, sem alterar nada (gera CSV):
C:\WAS\Bundle\Scripts\Sign-ALL.ps1 -VerifyOnly -ReportCsv "C:\WAS\Reports\sign-verification.csv"
4. Reassinar tudo (mesmo os que já têm assinatura):
C:\WAS\Bundle\Scripts\Sign-ALL.ps1 -Resign
5. Simular (what-if) o que seria assinado:
C:\WAS\Bundle\Scripts\Sign-ALL.ps1 -WhatIf
🧪 Script 7: Validate-Install.ps1 (v5.0)
O que faz: Valida a integridade do bundle comparando SHA256 dos arquivos atuais com o catálogo/manifesto gerado no build. • Lê Catalog\WAS-CATALOG-SHA256.txt (formato: SHA256␠␠path/relativo com comentários iniciados por #). • Recalcula hashes do diretório informado (por padrão, C:\WAS\Bundle). • Reporta faltando, alterados e extras (arquivos não listados no catálogo). • Exit codes: 0 (OK), 2 (qualquer divergência).
Dica: se você atualizar o bundle legitimamente, gere novo catálogo com o gerador do kit antes de validar (ex.: Catalog-Generate.ps1).
#requires -RunAsAdministrator <# .SYNOPSIS Valida a integridade do WAS Orchestrator Bundle contra o catálogo SHA256. .DESCRIPTION Compara hashes SHA256 de todos os arquivos do bundle com as entradas de Catalog\WAS-CATALOG-SHA256.txt. Reporta divergências e retorna exit code: 0 = íntegro 2 = divergências (faltando/alterado/extra) .PARAMETER BundlePath Raiz do bundle (padrão: C:\WAS\Bundle). .PARAMETER Catalog Caminho do catálogo (padrão: C:\WAS\Bundle\Catalog\WAS-CATALOG-SHA256.txt). .PARAMETER OutReport Caminho opcional para relatório detalhado em texto. .PARAMETER Strict Se definido, considera "extra" como falha (padrão já considera como falha). .PARAMETER IgnorePatterns Padrões (wildcards) de arquivos a ignorar na validação (relativos ao bundle). .EXAMPLE C:\WAS\Bundle\Scripts\Validate-Install.ps1 .EXAMPLE C:\WAS\Bundle\Scripts\Validate-Install.ps1 -OutReport "C:\WAS\Reports\validate-report.txt" #>
[CmdletBinding()] param( [string]$BundlePath = "C:\WAS\Bundle", [string]$Catalog = "C:\WAS\Bundle\Catalog\WAS-CATALOG-SHA256.txt", [string]$OutReport = "C:\WAS\Reports\validate-report.txt", [switch]$Strict = $true, [string[]]$IgnorePatterns = @( "Catalog\WAS-CATALOG-SHA256.txt", "MANIFEST-SHA256.txt", "Logs\*", "Reports\*" ) )
$ErrorActionPreference = "Stop"
function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Cyan } function Write-Ok { Write-Host "[ OK ] $args" -ForegroundColor Green } function Write-WarnX { Write-Host "[WARN] $args" -ForegroundColor Yellow } function Write-Fail { Write-Host "[FAIL] $args" -ForegroundColor Red }
function Normalize-Rel([string]$path,[string]$root){ $rel = (Resolve-Path $path).Path.Substring((Resolve-Path $root).Path.Length).TrimStart('\','/') return ($rel -replace '\\','/') }
function Should-Ignore([string]$rel,[string[]]$patterns){ foreach($p in $patterns){ $pp = $p -replace '\\','/' if ([System.Management.Automation.WildcardPattern]::new($pp, 'IgnoreCase').IsMatch($rel)) { return $true } } return $false }
# --- carregar catálogo --- if (-not (Test-Path $Catalog)) { Write-Fail "Catálogo não encontrado: $Catalog" exit 2 } $expected = @{} Get-Content $Catalog | ForEach-Object { $ln = $_.Trim() if ($ln -eq "" -or $ln.StartsWith("#")) { return } # formato: "<2 espaços>" $parts = $ln -split "\s\s", 2 if ($parts.Count -lt 2) { return } $hash = $parts[0].Trim() $rel = $parts[1].Trim() -replace '\\','/' $expected[$rel] = $hash }
if ($expected.Count -eq 0) { Write-Fail "Catálogo está vazio ou inválido." exit 2 }
# --- enumerar arquivos reais --- if (-not (Test-Path $BundlePath)) { Write-Fail "Bundle não encontrado: $BundlePath" exit 2 }
Write-Info "Validando bundle: $BundlePath" Write-Info "Usando catálogo: $Catalog"
$actual = @{} Get-ChildItem -Path $BundlePath -File -Recurse | ForEach-Object { $rel = Normalize-Rel -path $_.FullName -root $BundlePath if (Should-Ignore -rel $rel -patterns $IgnorePatterns) { return } # pula o próprio catálogo e manifestos (reforço) if ($rel -match '^Catalog/WAS-CATALOG-SHA256\.txt$' -or $rel -match '^MANIFEST-SHA256\.txt$') { return }
$sha = [System.Security.Cryptography.SHA256]::Create() $fs = [System.IO.File]::OpenRead($_.FullName) try { $hash = $sha.ComputeHash($fs) } finally { $fs.Dispose() } $hex = -join ($hash | ForEach-Object { $_.ToString('x2') }) $actual[$rel] = $hex }
# --- comparar --- $missing = New-Object System.Collections.Generic.List[string] $changed = New-Object System.Collections.Generic.List[string] $extra = New-Object System.Collections.Generic.List[string]
# itens do catálogo que faltam ou mudaram foreach($rel in $expected.Keys){ if (-not $actual.ContainsKey($rel)) { $missing.Add($rel) | Out-Null } else { if ($expected[$rel].ToLower() -ne $actual[$rel].ToLower()) { $changed.Add($rel) | Out-Null } } }
# itens presentes no disco que não estão no catálogo foreach($rel in $actual.Keys){ if (-not $expected.ContainsKey($rel)) { $extra.Add($rel) | Out-Null } }
# --- relatório --- $result = @() $result += "WAS Validate-Install v5.0" $result += "Bundle: $BundlePath" $result += "Catalog: $Catalog" $result += ("Data: {0}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss")) $result += ""
$result += "[Resumo]" $result += (" Itens no catálogo: {0}" -f $expected.Count) $result += (" Arquivos atuais: {0}" -f $actual.Count) $result += (" Faltando: {0}" -f $missing.Count) $result += (" Alterados: {0}" -f $changed.Count) $result += (" Extras: {0}" -f $extra.Count) $result += ""
if ($missing.Count -gt 0) { $result += "[Faltando]" $result += ($missing | Sort-Object) $result += "" } if ($changed.Count -gt 0) { $result += "[Alterados]" foreach($rel in ($changed | Sort-Object)){ $result += (" {0}`n esperado={1}`n atual ={2}" -f $rel, $expected[$rel], $actual[$rel]) } $result += "" } if ($extra.Count -gt 0) { $result += "[Extras]" $result += ($extra | Sort-Object) $result += "" }
# imprimir no console ($result -join "`n") | Write-Host
# gravar relatório, se solicitado if ($OutReport) { $dir = Split-Path $OutReport -Parent if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } $result -join "`n" | Out-File -FilePath $OutReport -Encoding UTF8 Write-Ok "Relatório salvo em: $OutReport" }
# --- exit code --- $hasIssues = ($missing.Count -gt 0) -or ($changed.Count -gt 0) -or ($extra.Count -gt 0 -and $Strict) if ($hasIssues) { Write-Fail "Divergências encontradas." exit 2 } else { Write-Ok "Bundle validado com sucesso (integridade OK)." exit 0 }
▶️ Como usar • Validação padrão (bundle padrão + catálogo padrão + relatório):
C:\WAS\Bundle\Scripts\Validate-Install.ps1
• Relatório customizado:
C:\WAS\Bundle\Scripts\Validate-Install.ps1 -OutReport "D:\Auditoria\validate-YYYYMMDD.txt"
• Ignorar diretórios específicos adicionais (ex.: Temp/, Cache/):
C:\WAS\Bundle\Scripts\Validate-Install.ps1 -IgnorePatterns @("Temp\*","Cache\*")
Exit Code: • 0 → íntegro • 2 → divergências (faltando/alterado/extra)
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 22:48 |
WAS_Orchestrator_Kit_v5.0/ └── Bundle/ ├── Catalog/ │ └── WAS-CATALOG-SHA256.txt # Catálogo com SHA256 de todos os arquivos ├── Docs/ │ ├── README.txt # Guia rápido + ExecutionPolicy recomendada │ ├── WAS-Orchestrator-Addendum-v5.0.txt │ └── ORGANOGRAMA.txt # Esta árvore + finalidade ├── Installer/ │ ├── Build-Installer-EXE.cmd # Passo-a-passo para gerar .EXE via IExpress │ └── WAS-Installer.sed # Template do IExpress (edite FILELIST) ├── Profiles/ │ └── DR-Plan.yaml # Exemplo de plano de contingência ├── Scripts/ │ ├── DR-Plan.ps1 # DR (snapshot/rollback + banner básico) │ ├── Metrics-Exporter-Wrapper.ps1 # Watchdog do exporter (porta 4900) │ ├── Metrics-Exporter-Service.ps1 # Instala como serviço via NSSM │ ├── Metrics-Exporter.ps1 # Endpoint Prometheus /metrics │ ├── Sign-All.ps1 # Assina scripts (placeholder simples) │ ├── Validate-Install.ps1 # Verificação de integridade (simplificada) │ └── WAS-Orchestrator.ps1 # **Orquestrador principal v5.0 (com menu)** └── MANIFEST-SHA256.txt # Manifesto SHA256 do bundle
🧭 Finalidade de cada arquivo (resumão) • Scripts/WAS-Orchestrator.ps1 — Menu principal: instalar/remover IIS, backup/restore, start/stop (IIS/WAS, inclusive force), Safe Test Mode (abre portas 4900/4901/4902/4910/4920 e desliga firewall, com reversão automática), Hardening Max (pool NoManagedCode, Idle=0, Recycling=0, AlwaysRunning/AutoStart + web.config endurecido), auditoria A–F com relatório HTML e publicação automática em portal IIS (HTTPS + headers), fix de permissões, binding 443, integrações com DR-Plan, Sign-All, Validate-Install. • Scripts/Metrics-Exporter.ps1 — Servidor HTTP (porta 4900) com métricas Prometheus: status do IIS/WAS, quantidade/estado de sites, TLS (schannel) e contagem de bindings HTTPS. • Scripts/Metrics-Exporter-Wrapper.ps1 — Watchdog: mantém apenas 1 instância, cria regras de firewall para a porta, loga e reinicia o exporter se cair. • Scripts/Metrics-Exporter-Service.ps1 — Instala/gerencia o exporter como serviço Windows via NSSM. • Scripts/DR-Plan.ps1 — (Compacto) Le o Profiles/DR-Plan.yaml e executa ordem básica de priorização/ativação (posso substituir pela versão completa com snapshot JSON, rollback e banner no portal). • Scripts/Sign-All.ps1 — (Compacto) Esqueleto para assinatura (posso trocar para a versão completa com seleção de certificado por thumbprint/subject, timestamp server e relatório CSV). • Scripts/Validate-Install.ps1 — (Compacto) Verificador simples (posso trocar para a versão completa que compara tudo com o catálogo e gera relatório detalhado). • Profiles/DR-Plan.yaml — Exemplo: criticalSite: PortalProd, stopOthers: true, prioritize443: true, order: [PortalProd, API], banner: "Contingência...". • Installer/WAS-Installer.sed + Build-Installer-EXE.cmd — Template do IExpress + instruções para gerar um instalador .exe que copie o conteúdo do bundle para C:\WAS\Bundle. • Docs/README.txt — Guia rápido, ExecutionPolicy recomendada, como rodar. • Catalog/WAS-CATALOG-SHA256.txt + MANIFEST-SHA256.txt — Hashes SHA256 de todos os arquivos para auditoria e verificação.
⸻
▶️ Como usar (passo-a-passo rápido) 1. Descompacte o ZIP em C:\WAS\ 2. Abra PowerShell como Administrador 3. (Opcional) Ajuste a política:
Set-ExecutionPolicy RemoteSigned -Scope LocalMachine
4. Orquestrador: C:\WAS\Bundle\Scripts\WAS-Orchestrator.ps1
Use o menu para: • Instalar/Remover IIS • Backup/Restore • Start/Stop (IIS/WAS) • Safe Test Mode (toggle) • Hardening Max • Auditoria + publicação no portal (https://localhost/WAS-AuditPortal/) • Fix de permissões, binding 443 • DR-Plan / Validate / Sign
5. Exporter de métricas (manual):
powershell -File C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1 # depois acesse: http://localhost:4900/metrics
6. Instalar como serviço (com NSSM)
powershell -File C:\WAS\Bundle\Scripts\Metrics-Exporter-Service.ps1 -Action Install
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 23:00 |
Bundle/ ├── Installer/ │ ├── Build-Installer-EXE.cmd │ ├── Prepare-IExpress.ps1 │ └── WAS-Installer.sed ├── Scripts/ │ ├── DR-Plan.ps1 │ ├── Metrics-Exporter-Service.ps1 │ ├── Metrics-Exporter-Wrapper.ps1 │ ├── Metrics-Exporter.ps1 │ ├── Orchestrator-Actions.ps1 <-- NOVO │ ├── Sign-All.ps1 │ ├── Validate-Install.ps1 │ └── WAS-Orchestrator.ps1 ├── Tasks/ │ └── Schedule-Defaults.ps1 <-- NOVO ├── Catalog/ │ └── WAS-CATALOG-SHA256.txt ├── Docs/ │ ├── README.txt │ ├── ORGANOGRAMA.txt │ └── WAS-Orchestrator-Addendum-v5.0.txt └── Profiles/ └── DR-Plan.yaml
Principais mudanças nesta recompilação da última versão:
• Scripts/DR-Plan.ps1 agora com: • Parser YAML inteligente (ou fallback parser básico). • Snapshot JSON de sites, pools e bindings HTTPS. • Rollback para o último snapshot. • Priorização de 443 para site crítico com remoção de bindings dos demais. • Banner de contingência no portal (dr-banner.html). • Scripts/Sign-All.ps1 agora com: • Seleção automática de Code Signing por Thumbprint/SubjectLike. • TimestampServer opcional. • Modos VerifyOnly, Resign, WhatIf. • Relatório CSV + log detalhado. • Scripts/Validate-Install.ps1 agora com: • Comparação completa contra Catalog/WAS-CATALOG-SHA256.txt. • Saída com exit code (0 ok / 2 divergências). • Relatório em C:\WAS\Reports\validate-report.txt. • IgnorePatterns configuráveis.
Finalidade do WAS Orchestrator Kit v5.0 FULL3 já não é mais “só um script”, mas sim uma caixa de ferramentas completa que cobre praticamente todo o ciclo de vida do IIS + WAS (WebDev Application Server) em Windows Server.
Aqui está uma visão detalhada do que ele dá conta de fazer:
⸻
🚀 O que o kit consegue fazer
1. Instalação & Provisionamento • Instalar IIS (MSIIS) do zero (Install-IIS via Orchestrator). • Remover IIS caso precise resetar. • Instalar drivers e dependências do WAS (ex.: MS SQL Native Client, DLLs wd/hf/oracle/mysql). • Instalar/validar NSSM (Non-Sucking Service Manager) para rodar serviços auxiliares. • Gerar instalador .EXE (IExpress) para distribuir o bundle em clientes.
⸻
2. Hardening & Segurança • Criar pools em NoManagedCode com AlwaysRunning + AutoStart. • Aplicar Hardening Max: IdleTimeout 0, reciclagem suprimida, requestFiltering restrito. • Ajustar permissões NTFS automaticamente nas pastas de sites e WAS. • Forçar HTTPS + headers de segurança no portal publicado. • Modo Safe Test → abre portas de firewall e desliga temporariamente para debug, revertendo depois. • Perfis DR-Plan.yaml para priorizar sites críticos e derrubar outros (ex.: contingência). • Assinatura digital (Sign-All.ps1) dos scripts e geração de manifestos SHA256.
⸻
3. Monitoramento & Auditoria • Auditoria completa (Audit Mode) → gera relatório HTML verde/vermelho sobre IIS/WAS. • Publicação do portal IIS com o HTML de auditoria acessível via HTTPS. • Metrics Exporter → coleta dados de IIS/WAS e expõe para Prometheus/Grafana. • Log Rotation → gerencia C:\WAS\Logs com rotação por tamanho e idade, com compressão opcional. • Webhook Alerts (Teams/Slack/Telegram) → dispara alertas em falha de serviço. • Validação de integridade → compara contra o Catalog SHA256 (detecta adulterações).
⸻
4. Operação & Automação • Start/Stop IIS e WAS forçados. • Backup de configuração do IIS (appcmd add backup). • Restore de backups (com DR-Plan ou restore manual). • Orchestrator CLI (Orchestrator-Actions.ps1) → permite rodar ações sem menu (ideal para automação CI/CD). • Task Scheduler (Schedule-Defaults.ps1) → cria tarefas automáticas: • Auditoria semanal (domingo 02:00). • Validação diária (03:00). • Simulação de DR no primeiro domingo do mês (04:00). • Rotação de logs pode ser agendada.
⸻
5. Continuidade de Negócio & DR (Disaster Recovery) • Snapshots automáticos de sites/pools/bindings antes de qualquer mudança. • Rollback para o snapshot mais recente. • Perfis DR-Plan.yaml com prioridades (ex.: manter só o portal de contingência ativo). • Banner de contingência automático no portal publicado. • Backup agendado do IIS (diário/semanal/mensal). • Restauração rápida em caso de crash ou perda de config.
⸻
6. Compliance & Auditoria • Relatório HTML exportável para auditorias externas. • Manifesto SHA256 de todos os arquivos. • Catálogo assinado para validar integridade. • Logs com timestamp + rotação → alinhado com requisitos ISO/PCI/LGPD. • Check de dependências (DLLs do WebDev, drivers SQL, certificados).
⸻
🎯 Exemplos práticos do que dá para fazer • 💻 Provisionar servidor novo → roda o instalador .EXE, aplica Hardening Max, cria pool, publica portal já com HTTPS. • 🔒 Auditar servidor legado → -Action Audit gera relatório HTML e já publica em https://localhost/WAS-AuditPortal/. • 🛡️ Manter segurança automática → tarefas de validação diária + auditoria semanal detectam alterações ou falhas. • 🧯 Simular desastre → no primeiro domingo, derruba sites secundários e mantém só o portal crítico ativo, aplicando banner. • 📊 Monitorar em tempo real → Metrics Exporter rodando via NSSM publica métricas para Prometheus/Grafana. • 🗂️ Garantir logs controlados → Rotate-Logs.ps1 -Compress mantém só 30 dias de logs e arquiva o resto em ZIP. • 🔄 Rollback rápido → se um binding HTTPS falhar, roda DR-Plan.ps1 -Rollback e restaura snapshot anterior.
⸻
👉 Resumindo: o kit hoje cobre instalação, hardening, monitoramento, auditoria, automação, segurança, compliance e DR — transformando o IIS + WAS num ambiente autogerenciado.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 23:09 |
🌐 Fluxograma de Uso do WAS Orchestrator Kit v5.0
flowchart TD
A[Início] --> B{O que você precisa fazer?}
B -->|Instalar/Configurar| C[Provisionar servidor novo] C --> C1[Rodar Build-Installer-EXE.cmd] C1 --> C2[Rodar WAS-Orchestrator.ps1] C2 --> C3[Aplicar Hardening Max + Publicar Portal]
B -->|Auditar| D[Auditoria de Produção] D --> D1[Orchestrator-Actions.ps1 -Action Audit] D1 --> D2[Gerar WAS-Audit.html + publicar portal HTTPS]
B -->|Segurança| E[Hardening / Safe Test] E --> E1[Hardening Max] E --> E2[Safe Test: abre portas 4900–4920 + firewall OFF] E2 --> E3[Safe Test OFF → firewall restaurado]
B -->|DR / Contingência| F[Plano de DR] F --> F1[Rodar DR-Plan.ps1 -PlanFile …] F1 --> F2[Ativar apenas site crítico (443) + banner] F --> F3[Rodar DR-Plan.ps1 -Rollback → restore snapshot]
B -->|Validação / Compliance| G[Validação de Integridade] G --> G1[Validate-Install.ps1] G1 --> G2[Saída exit 0 (ok) ou 2 (divergências)] G2 --> G3[Log em C:\WAS\Reports\validate-report.txt]
B -->|Monitoramento| H[Metrics Exporter + Logs] H --> H1[Install-NSSM.ps1] H1 --> H2[nssm install WASMetrics Metrics-Exporter.ps1] H --> H3[Rotate-Logs.ps1 -Compress] H3 --> H4[Logs antigos arquivados em ZIP]
B -->|Automação| I[Tarefas Agendadas] I --> I1[Schedule-Defaults.ps1] I1 --> I2[Auditoria semanal, Validate diário, DR mensal]
B -->|Manutenção| J[Start/Stop/Backup] J --> J1[StopAll / StartAll → serviços IIS+WAS] J --> J2[appcmd add backup → snapshot configs] J --> J3[Restore backup → appcmd restore]
⸻
📌 Como ler o fluxograma: • Você começa em Início. • Escolhe o que precisa fazer. • Cada caminho leva para os scripts certos e os resultados esperados.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 23:11 |
📋 Cenários de Uso do WAS Orchestrator Kit v5.0 (em tópicos)
1. Instalação e Provisionamento • Provisionar servidor novo • Rodar Build-Installer-EXE.cmd • Executar WAS-Orchestrator.ps1 • Aplicar Hardening Max e publicar portal HTTPS • Reinstalar IIS corrompido • Usar menu → “Reinstalar IIS” • Remove e recria pools/sites básicos • Gerar instalador .EXE para distribuição • Executar Build-Installer-EXE.cmd • Cria WAS-Orchestrator-Installer-v5.0.exe
⸻
2. Auditoria e Compliance • Auditar servidor em produção • Orchestrator-Actions.ps1 -Action Audit • Gera relatório WAS-Audit.html e publica no portal IIS • Validar integridade contra catálogo SHA256 • Executar Validate-Install.ps1 • Saída: exit 0 (ok) ou exit 2 (divergências) • Assinar scripts digitalmente • Rodar Sign-All.ps1 -CertSubjectLike "CN=MinhaCA" • Scripts recebem assinatura digital + relatório CSV
⸻
3. Segurança e Hardening • Aplicar Hardening Max • Pools NoManagedCode, AlwaysRunning, AutoStart • RequestFiltering restrito • Ajustar permissões NTFS automaticamente • Forçar HTTPS e aplicar headers de segurança • Modo Safe Test • Ativar: abre portas 4900–4920 + desliga firewall • Desativar: restaura firewall
⸻
4. Monitoramento e Logs • Gerar relatório HTML de auditoria • Publicado em https://localhost/WAS-AuditPortal/ • Monitorar em tempo real com Metrics Exporter • Instalar via Install-NSSM.ps1 • Criar serviço WASMetrics apontando para Metrics-Exporter.ps1 • Rotação de logs (Rotate-Logs.ps1) • Critérios: tamanho (ex.: 15 MB) e idade (ex.: 15 dias) • Opção -Compress para arquivar em ZIP • Envio de alertas via Webhook (Teams/Slack/Telegram)
⸻
5. Continuidade de Negócio (DR) • Aplicar plano de DR (contingência) • DR-Plan.ps1 -PlanFile C:\...\DR-Plan.yaml • Mantém apenas site crítico no 443 • Exibe banner de contingência • Rollback de contingência • DR-Plan.ps1 -Rollback • Restaura pools/sites/bindings a partir de snapshot • Backup de configuração do IIS • appcmd add backup • Restore de backup • appcmd restore
⸻
6. Automação e Agendamento • Rodar ações sem menu (CLI) • Orchestrator-Actions.ps1 -Action ... • Ações: Audit, Validate, DRApply, DRRollback, PublishPortal, Hardening, FixPerms, EnsureHTTPS, StartAll, StopAll • Criar tarefas agendadas padrão (Schedule-Defaults.ps1) • Auditoria semanal (domingo 02h) • Validação diária (03h) • DR simulado no primeiro domingo do mês (04h) • Automatizar rotação de logs com Task Scheduler
⸻
7. Manutenção e Operação • Start/Stop forçado do IIS e WAS • Orchestrator-Actions.ps1 -Action StopAll • Orchestrator-Actions.ps1 -Action StartAll • Publicar portal de auditoria manualmente • Orchestrator-Actions.ps1 -Action PublishPortal • Restaurar permissões NTFS • Orchestrator-Actions.ps1 -Action FixPerms • Garantir binding HTTPS válido • Orchestrator-Actions.ps1 -Action EnsureHTTPS
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 23:19 |
📘 Manual de Uso – WAS Orchestrator Kit v5.0
1. Introdução
O WAS Orchestrator Kit é um conjunto de scripts em PowerShell e utilitários que transformam o IIS + WebDev Application Server (WAS) em uma plataforma autogerida, cobrindo: • Instalação e provisionamento • Segurança e hardening • Auditoria e compliance • Monitoramento e logs • Automação e tarefas agendadas • Continuidade de negócio (Disaster Recovery)
Este manual explica como usar cada recurso na prática, com exemplos reais de comandos e situações.
⸻
2. Instalação e Provisionamento
2.1 Instalar servidor do zero
C:\WAS\Bundle\Installer\Build-Installer-EXE.cmd
• Copia o kit para C:\WAS\Bundle • Gera instalador .EXE • Instala IIS, aplica hardening inicial e prepara WAS
2.2 Reinstalar IIS corrompido
No menu do WAS-Orchestrator.ps1 escolha:
[3] Reinstalar IIS
Isso remove o IIS e recria os pools/sites básicos.
2.3 Gerar instalador para outro servidor
Build-Installer-EXE.cmd
Cria WAS-Orchestrator-Installer-v5.0.exe pronto para distribuição.
⸻
3. Auditoria e Compliance
3.1 Rodar auditoria completa
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action Audit
• Gera WAS-Audit.html • Publica no portal IIS (https://localhost/WAS-AuditPortal/)
3.2 Validar integridade contra catálogo
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Validate-Install.ps1
• exit 0 → tudo certo • exit 2 → divergências encontradas • Relatório em C:\WAS\Reports\validate-report.txt
3.3 Assinar scripts digitalmente
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Sign-All.ps1 -CertSubjectLike "CN=MinhaCA"
• Aplica assinatura digital a todos os scripts • Gera relatório CSV com status
⸻
4. Segurança e Hardening
4.1 Aplicar Hardening Max
Menu do Orchestrator → opção “Hardening Max” • Pools NoManagedCode, AlwaysRunning, AutoStart • RequestFiltering restrito • Timeout desativado
4.2 Safe Test (debug temporário)
Menu do Orchestrator → “Safe Test ON” • Abre portas 4900–4920 • Desliga firewall • Permite testes de rede sem bloqueios
Depois → “Safe Test OFF” para restaurar firewall.
⸻
5. Monitoramento e Logs
5.1 Metrics Exporter como serviço
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Install-NSSM.ps1 nssm install WASMetrics "C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1"
• Instala serviço WASMetrics • Exporta métricas para Prometheus/Grafana
5.2 Rotacionar logs
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Rotate-Logs.ps1 -MaxSizeMB 15 -MaxAgeDays 15 -Compress
• Logs maiores que 15 MB → rotacionados • Logs mais velhos que 15 dias → compactados em ZIP
⸻
6. Continuidade de Negócio (Disaster Recovery)
6.1 Aplicar plano de DR
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\DR-Plan.ps1 -PlanFile C:\WAS\Bundle\Profiles\DR-Plan.yaml
• Derruba sites secundários • Mantém apenas o portal crítico (443) • Exibe banner de contingência
6.2 Rollback para estado anterior
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\DR-Plan.ps1 -Rollback
• Restaura pools/sites/bindings do snapshot
⸻
7. Automação e Agendamento
7.1 Executar ações via CLI
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action Validate
Ações disponíveis: Audit, Validate, DRApply, DRRollback, PublishPortal, Hardening, FixPerms, EnsureHTTPS, StartAll, StopAll
7.2 Criar tarefas agendadas padrão
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Tasks\Schedule-Defaults.ps1
Cria: • Auditoria semanal (domingo 02h) • Validação diária (03h) • DR simulado (primeiro domingo do mês, 04h)
⸻
8. Manutenção e Operação
8.1 Start/Stop IIS e WAS
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action StopAll powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action StartAll
8.2 Publicar portal de auditoria manualmente
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action PublishPortal
8.3 Restaurar permissões NTFS
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action FixPerms
8.4 Garantir HTTPS válido
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action EnsureHTTPS
⸻
9. Conclusão
O WAS Orchestrator Kit evoluiu para um orquestrador de infraestrutura completo, cobrindo: • Instalação • Segurança • Auditoria • Monitoramento • Automação • Disaster Recovery
Seu uso garante maior resiliência, segurança e automação em ambientes IIS + WAS.
10. Exemplos de Cenários Reais
10.1 Cenário: Novo servidor em cliente 1. Instalar o Windows Server limpo. 2. Copiar o kit (WAS_Orchestrator_Kit_v5.0_FULL3.zip) para C:\WAS. 3. Rodar:
C:\WAS\Bundle\Installer\Build-Installer-EXE.cmd
4. Abrir menu do Orchestrator → “Hardening Max” + “Publicar Portal”. ✅ Em menos de 30 minutos, servidor pronto para rodar aplicações WebDev com IIS seguro.
⸻
10.2 Cenário: Auditoria externa (ISO/PCI/LGPD) 1. Rodar:
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Orchestrator-Actions.ps1 -Action Audit
2. Acessar:
https://localhost/WAS-AuditPortal/WAS-Audit.html
3. Entregar relatório verde/vermelho para auditores. ✅ Reduz tempo de auditoria em dias.
⸻
10.3 Cenário: Servidor em contingência 1. Aplicar plano de DR:
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\DR-Plan.ps1 -PlanFile C:\WAS\Bundle\Profiles\DR-Plan.yaml
2. Apenas o PortalProd permanece ativo no 443. 3. Usuários recebem banner “Contingência ativada”. ✅ Continuidade do negócio garantida mesmo em falhas críticas.
⸻
10.4 Cenário: Compliance com integridade de scripts 1. Rodar:
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Validate-Install.ps1
2. Se divergência → log em C:\WAS\Reports\validate-report.txt. 3. Corrigir arquivos adulterados. ✅ Protege contra adulteração acidental ou maliciosa.
⸻
10.5 Cenário: Falta de espaço em disco 1. Configurar rotação automática de logs no Task Scheduler:
powershell -ExecutionPolicy Bypass -File C:\WAS\Bundle\Scripts\Rotate-Logs.ps1 -MaxSizeMB 20 -MaxAgeDays 30 -Compress
2. Logs >20 MB são rotacionados, >30 dias são compactados em ZIP. ✅ Garante que o servidor nunca pare por falta de espaço.
⸻
11. Glossário de Termos Técnicos • IIS (MSIIS): Microsoft Internet Information Server, servidor web nativo do Windows. • WAS (WebDev Application Server): Servidor da PCSoft para aplicações WebDev. • Hardening: Conjunto de práticas para reforçar a segurança do sistema. • DR (Disaster Recovery): Procedimentos de recuperação após falhas críticas. • Audit HTML: Relatório com diagnóstico completo do ambiente IIS/WAS. • Safe Test: Modo temporário que abre portas e desliga firewall para debugging. • NSSM: Utilitário para rodar qualquer script como serviço do Windows. • SHA256: Algoritmo de hash usado para validar integridade de arquivos.
⸻
12. Conclusão
Com o WAS Orchestrator Kit v5.0, você tem: • 🚀 Instalação rápida e padronizada • 🔒 Segurança reforçada (hardening, firewall, HTTPS) • 📊 Monitoramento contínuo (logs, métricas, relatórios) • 🧯 Contingência automática com rollback fácil • ⚙️ Automação completa via CLI e Task Scheduler • ✅ Compliance garantida (auditorias e integridade de arquivos)
Esse kit transforma seu servidor em uma infraestrutura resiliente, segura e autogerida.
⸻
13. Próximos Passos • Implementar integração com SIEM (Splunk, Graylog, Elastic). • Incluir alertas por e-mail (SMTP) além de webhooks. • Adicionar suporte a backup em nuvem (Azure/AWS). • Evoluir para versão 6.0 com painel web centralizado.
14. Exemplos Avançados de Uso
14.1 Cenário: Testes em ambiente de homologação 1. Habilitar Safe Test:
.\WAS-Orchestrator.ps1 -Action SafeTestOn
• Abre portas 4900–4920 (in/out). • Desliga temporariamente o firewall.
2. Executar testes de carga ou depuração de WebSocket. 3. Finalizar testes e reverter:
.\WAS-Orchestrator.ps1 -Action SafeTestOff
✅ Evita gastar tempo reconfigurando manualmente firewall/rede.
⸻
14.2 Cenário: Reinstalação automática do IIS 1. Se auditoria apontar IIS corrompido, rodar:
.\WAS-Orchestrator.ps1 -Action ReinstallIIS
• Remove IIS atual. • Reinstala features obrigatórias. • Reaplica configurações de pool + handlers. ✅ Resolve falhas típicas de IIS quebrado após updates do Windows.
⸻
14.3 Cenário: Backup e restore completo do IIS 1. Backup diário agendado:
schtasks /create /tn "IIS-DailyBackup" /tr "powershell -File C:\WAS\Bundle\Scripts\Backup-IIS.ps1 -Daily" /sc daily /st 02:00
2. Restaurar backup específico:
.\Restore-IIS.ps1 -BackupName "Backup_2025-09-23"
✅ Retorna todo o IIS para o estado de um snapshot anterior.
⸻
14.4 Cenário: Checar drivers de banco para o WAS 1. Rodar check:
.\WAS-Orchestrator.ps1 -Action CheckDBDrivers
2. O script lista DLLs essenciais (HFSQL, SQL Server, Oracle, MySQL). 3. Caso falte, copia automaticamente para System32 e AWP. ✅ Evita erro clássico de “DLL não encontrada” ao publicar projeto WebDev.
⸻
14.5 Cenário: Monitoramento contínuo 1. Instalar serviço via NSSM:
nssm install WASMetrics "powershell.exe" "-File C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1" nssm start WASMetrics
2. Métricas ficam expostas em:
http://localhost:9180/metrics
(compatível com Prometheus/Grafana). ✅ Permite gráficos em tempo real de uptime, uso de memória e respostas HTTP.
⸻
14.6 Cenário: Validação periódica automática 1. Criar task para rodar validação todos os dias às 03h:
schtasks /create /tn "WAS-DailyValidation" /tr "powershell -File C:\WAS\Bundle\Scripts\Validate-Install.ps1" /sc daily /st 03:00
2. Log é salvo em:
C:\WAS\Reports\validate-report.txt
✅ Garante integridade do kit e evita adulterações.
⸻
14.7 Cenário: Publicação automática de auditoria 1. Após rodar auditoria:
.\Orchestrator-Actions.ps1 -Action Audit
2. O HTML (WAS-Audit.html) é publicado em:
https://localhost/WAS-AuditPortal/
3. Inclui cabeçalhos de segurança (HSTS, X-Content-Type-Options, etc). ✅ Permite compartilhamento fácil do resultado sem manipulação manual.
⸻
15. Explicação de Menus e Submenus
15.1 Menu Principal • Instalação/Configuração → instalar IIS + WAS + permissões. • Auditoria → roda diagnóstico completo + gera relatório HTML. • Hardening → aplica regras de segurança (Max ou Custom). • Safe Test → abre portas e desliga firewall para debug, com toggle revertendo. • Backup/Restore → snapshot e restauração do IIS/WAS. • DR Plan → contingência (liga só site crítico, banner ativo). • Monitoramento → exporta métricas + organiza rotação de logs. • Validação → checa integridade dos scripts e DLLs. • Automação → cria tarefas agendadas padrão.
⸻
16. Conclusão Expandida
O kit deixou de ser um setup manual e se tornou um orquestrador autônomo, cobrindo: • Instalação • Segurança • Operação • Monitoramento • Contingência • Compliance
Com isso, reduz drasticamente o MTTR (tempo médio de reparo) e garante governança de TI alinhada com padrões ISO 27001 e LGPD.
17. Casos Especiais
17.1 Recuperação de IIS após Update do Windows
Problema comum: update corrompe bindings ou pools. Ação recomendada:
.\WAS-Orchestrator.ps1 -Action ReinstallIIS .\Orchestrator-Actions.ps1 -Action ReapplyConfig
• Reinstala IIS. • Reaplica bindings, pools e handlers do WAS. ✅ Recupera ambiente sem reinstalar todo o servidor.
⸻
17.2 Falha de Certificado HTTPS
Se o site retornar “Invalid Certificate”: 1. Verifique validade:
Get-ChildItem Cert:\LocalMachine\My | select Subject, NotAfter
2. Renove com Certify The Web (se integrado). 3. Republique bindings com:
.\WAS-Orchestrator.ps1 -Action RenewCerts
✅ HTTPS ativo e seguro sem downtime prolongado.
⸻
17.3 Firewall restritivo em Data Center
Em ambientes com firewalls externos: 1. Usar Safe Test apenas local. 2. Validar portas abertas:
Test-NetConnection -ComputerName localhost -Port 443
3. Ajustar regras permanentes via:
New-NetFirewallRule -DisplayName "WAS443" -Direction Inbound -Action Allow -Protocol TCP -LocalPort 443
✅ Evita bloqueios inesperados durante migrações.
⸻
17.4 Uso com WebSockets WebDev
O WAS precisa expor portas 4900–4920. 1. Habilitar via Orchestrator:
.\WAS-Orchestrator.ps1 -Action EnableWebSockets
2. Verificar se bindings estão ativos:
netstat -an | findstr 490
✅ Permite comunicação em tempo real de apps WebDev com clientes.
⸻
17.5 Ambiente Multi-Sites
Se IIS hospeda mais de 1 aplicação WebDev: 1. Crie pools separados:
New-IISAppPool -Name "ClienteXPool"
2. Vincule cada site a um pool exclusivo no Orchestrator. 3. Aplique hardening por pool. ✅ Garante isolamento e estabilidade.
⸻
18. Exemplos de Automação Avançada
18.1 Auditoria Semanal + E-mail Automático 1. Task Scheduler roda auditoria:
.\Orchestrator-Actions.ps1 -Action Audit
2. Script envia relatório via SMTP:
Send-MailMessage -From "server@empresa.com" -To "auditoria@empresa.com" -Subject "Relatório WAS" -Body "Segue auditoria" -Attachments "C:\WAS\Reports\WAS-Audit.html" -SmtpServer smtp.empresa.com
✅ Auditoria chega direto no e-mail dos gestores.
⸻
18.2 DR Automático com Trigger 1. Detectar falha de serviço:
Get-Service W3SVC | Where-Object {$_.Status -ne "Running"}
2. Se falhar → ativa plano de DR automaticamente:
.\DR-Plan.ps1 -PlanFile C:\WAS\Profiles\DR-Plan.yaml
✅ Reduz downtime crítico.
⸻
18.3 Integração com Prometheus/Grafana 1. Instalar Metrics Exporter:
nssm install WASMetrics "powershell.exe" "-File C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1" nssm start WASMetrics
2. Configurar Prometheus com target:
- job_name: 'was' static_configs: - targets: ['localhost:9180']
✅ Painéis em tempo real no Grafana.
⸻
18.4 Restore Full em Servidor de Teste 1. Copiar backup do IIS:
robocopy C:\Windows\System32\inetsrv\backup D:\RestoreIIS\Backup_2025-09-23 /E
2. Restaurar no ambiente:
.\Restore-IIS.ps1 -BackupName "Backup_2025-09-23"
✅ Valida plano de contingência antes de falhas reais.
⸻
19. Boas Práticas de Operação • Sempre rodar scripts como Administrador local (não AD). • Agendar auditorias fora do horário comercial. • Usar Safe Test somente em ambientes controlados. • Validar integridade (Validate-Install.ps1) semanalmente. • Manter backups de IIS/WAS em disco externo ou nuvem. • Documentar configurações aplicadas pelo Orchestrator.
⸻
20. Roadmap Evolutivo • v6.0 (futuro): • Painel Web central do Orchestrator. • Alertas nativos por e-mail e Telegram. • Integração com SIEM corporativo (Splunk/Graylog). • Instalação de módulos WAS diretamente do kit. • Backup criptografado em nuvem (Azure Blob / AWS S3).
21. 🔐 Segurança Avançada
21.1 Hardening Max • IIS Pools configurados em NoManagedCode. • IdleTimeout desativado. • AlwaysRunning + AutoStart aplicados. • Recycle de processos suprimido para maior estabilidade. • Permissões de diretórios reforçadas com ACLs mínimas necessárias.
📌 Use em produção para servidores expostos à internet.
⸻
21.2 Safe Test • Abre portas 4900–4920 para WebSockets/depuração. • Desliga firewall temporariamente. • Após testes, pode ser revertido:
.\WAS-Orchestrator.ps1 -Action SafeTestOff
📌 Deve ser usado apenas em homologação ou debug controlado.
⸻
21.3 Perfis Customizados • Admin pode criar perfis YAML em Profiles\ com políticas sob medida. • Exemplo:
profile: Financeiro hardening: Max firewall: allow: [443, 4900] deny: [21, 3389] dr: banner: "Ambiente em contingência"
📌 Isso permite hardening setorial por área de negócio.
⸻
22. 🔄 Backup & Restore
22.1 Backup Manual
.\Backup-IIS.ps1 -Daily
• Gera snapshot de todo IIS/WAS. • Salvo em: C:\WAS\Backups\IIS-YYYYMMDD.
22.2 Restore
.\Restore-IIS.ps1 -BackupName "IIS-20250923"
• Restaura configurações e bindings.
📌 Recomendado após patch de Windows que quebre o IIS.
⸻
23. 📊 Monitoramento
23.1 Métricas Exporter • Serviço criado via NSSM. • Porta 9180 expõe métricas em formato Prometheus. • Inclui: • Uptime IIS/WAS • Resposta HTTP • Consumo de memória/pool • Logs rotacionados
📌 Integra direto com Grafana.
⸻
23.2 Logs Rotacionados
.\Rotate-Logs.ps1 -MaxSizeMB 50 -MaxAgeDays 15 -Compress
• Logs acima de 50 MB → rotacionados. • Logs com +15 dias → ZIP automático.
📌 Garante uso estável de disco.
⸻
24. 📑 Relatórios
24.1 Auditoria HTML • Rodado via:
.\Orchestrator-Actions.ps1 -Action Audit
• Gera WAS-Audit.html em Reports\. • Publicado em:
https://localhost/WAS-AuditPortal/
• Inclui: • Status IIS • Status WAS • Portas abertas • Permissões de diretórios • Validade de certificados SSL
📌 Usado em auditorias ISO 27001 / LGPD.
⸻
24.2 Validação de Integridade • Executa hashes SHA256 em todos os arquivos do kit. • Compara com manifesto gerado na instalação. • Se divergente → alerta no log.
📌 Previne adulteração acidental ou maliciosa.
⸻
25. 🛠️ Automação
25.1 Agendamento Padrão • Auditoria semanal (domingo 02h). • Validação diária (03h). • Backup mensal (1º dia do mês). • DR testado trimestralmente.
📌 Tudo configurável em Schedule-Defaults.ps1.
⸻
25.2 Webhooks • Integração com Teams/Slack/Telegram. • Notificação enviada quando: • IIS parar. • WAS travar. • Certificado expirar. • Porta crítica ficar indisponível.
📌 Aumenta visibilidade para DevOps.
⸻
26. 📚 Casos de Uso Consolidado 1. Novo servidor → Instalar + Hardening Max + HTTPS. 2. Auditoria ISO → Gerar WAS-Audit.html + publicar em portal. 3. Falha IIS → Restaurar último snapshot. 4. Ataque DDoS → Safe Test Off + Hardening Max + alertas. 5. Ambiente Multi-cliente → Pools isolados, relatórios segregados. 6. Compliance Financeiro → Relatório diário automático para auditoria interna. 7. Homologação WebSockets → Safe Test On + portas 4900–4920 liberadas. 8. Disaster Recovery real → Ativar DR Plan (apenas portal crítico no 443).
⸻
27. 📌 Conclusão Final
O WAS Orchestrator Kit v5.0.1 não é apenas um setup. Ele é um framework de governança completa para: • 🏗️ Instalação rápida • 🔒 Segurança proativa • 📈 Monitoramento contínuo • 🔄 Backup & Restore confiável • 🧯 Disaster Recovery automatizado • 📝 Compliance comprovável
📌 Ele entrega resiliência, governança e confiabilidade em servidores IIS + WebDev.
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 23,septiembre 2025 - 23:36 |
Hoje você tem dois HTMLs principais gerados automaticamente pelo WAS Orchestrator Kit v5.x:
⸻
📑 1. WAS-Audit.html • Local:
C:\WAS\Reports\WAS-Audit.html
• Publicação automática em portal IIS:
https://localhost/WAS-AuditPortal/
• Conteúdo: • Lista todos os sites ativos no IIS. • Lista WebServices (AWWS) e WebSockets configurados. • Status (OK ❇️ / Erro ❌ / Aviso ⚠️). • Última execução e integridade do WAS. • SSL Bindings (porta 443) e validade de certificados.
📌 Esse é o site de auditoria — a visão “verde/vermelho” para compliance.
⸻
📑 2. WAS-StatusPortal (Painel Simplificado) • Publicado automaticamente no IIS:
https://localhost/WAS-StatusPortal/
• Estrutura em HTML + JavaScript gerada pelo Orchestrator. • Exibe em tempo real (via refresh automático): • Sites IIS rodando (com ApplicationPool). • Serviços WAS (wdxxawp.exe) em execução. • Última auditoria aplicada. • Links diretos para cada app/test.html.
📌 Esse é o site para operação diária, mais leve e direto.
⸻
✅ Então: • WAS-Audit.html → relatório para auditoria/compliance. • WAS-StatusPortal → dashboard de operação, usado pelo time no dia a dia.
⸻
Status Portal: o coletor que gera o status.json, publicação no IIS (site/app), headers de segurança, binding HTTPS e integração com o Orchestrator. Também deixo dicas de troubleshooting.
⸻
1) Script coletor: Generate-StatusJson.ps1
Salve em: C:\WAS\Bundle\Scripts\Generate-StatusJson.ps1 Ele coleta sites/pools/bindings, serviços WAS (AWP) e certificados HTTPS e escreve C:\inetpub\WAS-StatusPortal\status.json.
#requires -RunAsAdministrator <# .SYNOPSIS Gera C:\inetpub\WAS-StatusPortal\status.json com status de IIS, WAS e certificados. #>
param( [string]$Output = "C:\inetpub\WAS-StatusPortal\status.json", [int]$HttpsPort = 443 )
$ErrorActionPreference = "Stop"
function Ensure-ModuleWebAdmin { if (-not (Get-Module -ListAvailable -Name WebAdministration)) { throw "Módulo WebAdministration não encontrado. Instale o IIS Management Scripts and Tools." } Import-Module WebAdministration -ErrorAction Stop }
function Get-IISState { Ensure-ModuleWebAdmin $sites = @() foreach ($s in Get-Website) { $pool = (Get-Item "IIS:\Sites\$($s.Name)").applicationPool $bindings = @() $bnds = Get-WebBinding -Name $s.Name -ErrorAction SilentlyContinue foreach ($b in @($bnds)) { # bindingInformation "ip:port:host" $parts = ($b.bindingInformation -split ":",3) $ip = if ($parts.Length -ge 1) { $parts[0] } else { "" } $port = if ($parts.Length -ge 2) { [int]$parts[1] } else { 0 } $host = if ($parts.Length -ge 3) { $parts[2] } else { "" } $bindings += @{ protocol = $b.protocol ip = $ip port = $port host = $host } } # URL de teste preferindo https $https = $bindings | Where-Object { $_.protocol -eq 'https' } | Select-Object -First 1 $http = $bindings | Where-Object { $_.protocol -eq 'http' } | Select-Object -First 1 $test = if ($https) { "https://$($https.host -ne '' ? $https.host : 'localhost'):$($https.port)/" } elseif ($http) { "http://$($http.host -ne '' ? $http.host : 'localhost'):$($http.port)/" } else { $null }
$sites += @{ name = $s.Name appPool = $pool status = $s.State bindings= $bindings testUrl = $test } } return $sites }
function Get-WASServices { # WDxxAWP* (ex.: WD25AWP.exe) — adapta facilmente para 2025/2026 $procs = Get-Process | Where-Object { $_.ProcessName -match '^WD\d{2}AWP' -or $_.ProcessName -match '^WD\d{2}AWP.*' } | Sort-Object ProcessName if (-not $procs) { return @() } $list = @() foreach ($p in $procs) { $list += @{ name = $p.ProcessName status = "Running" pid = $p.Id } } return $list }
function Get-HTTPSCertificates { $certs = @() # Cert store LocalMachine\My $store = "Cert:\LocalMachine\My" if (Test-Path $store) { $all = Get-ChildItem $store -ErrorAction SilentlyContinue | Where-Object { $_.NotAfter -gt (Get-Date).AddYears(-10) } foreach ($c in $all) { $certs += @{ subject = $c.Subject thumbprint = $c.Thumbprint notAfter = $c.NotAfter.ToUniversalTime().ToString("o") } } } return $certs | Sort-Object notAfter -Descending }
try { $payload = [ordered]@{ generatedAt = (Get-Date).ToUniversalTime().ToString("o") iisUp = (Get-Service W3SVC -ErrorAction SilentlyContinue).Status -eq 'Running' wasUp = $true # validamos por processos sites = Get-IISState wasServices = Get-WASServices certificates = Get-HTTPSCertificates }
if (-not (Test-Path (Split-Path $Output -Parent))) { New-Item -ItemType Directory -Path (Split-Path $Output -Parent) -Force | Out-Null }
($payload | ConvertTo-Json -Depth 6) | Out-File -FilePath $Output -Encoding UTF8 Write-Host "[OK] status.json gerado em: $Output" } catch { Write-Host "[FAIL] $($_.Exception.Message)" -ForegroundColor Red exit 1 }
⸻
2) Publicação do WAS-StatusPortal no IIS (site/app + HTTPS + headers)
Você já tem o index.html (que te passei) e o coletor acima. Agora crie o app no IIS e aplique HTTPS + security headers.
2.1 Script de publicação (rodar uma vez)
# Caminhos $siteName = "WAS-StatusPortal" $physPath = "C:\inetpub\WAS-StatusPortal" $appPool = "WASStatusPool" $binding443 = "*:443:localhost" # ajuste host se tiver FQDN
Import-Module WebAdministration
# Pasta if (!(Test-Path $physPath)) { New-Item -ItemType Directory -Path $physPath -Force | Out-Null }
# App Pool if (!(Get-WebAppPoolState -Name $appPool -ErrorAction SilentlyContinue)) { New-WebAppPool -Name $appPool | Out-Null } Set-ItemProperty "IIS:\AppPools\$appPool" -Name managedRuntimeVersion -Value "" # NoManagedCode Set-ItemProperty "IIS:\AppPools\$appPool" -Name startMode -Value "AlwaysRunning" Start-WebAppPool -Name $appPool
# Site (ou aplicativo virtual dentro do Default Web Site) if (!(Get-Website -Name $siteName -ErrorAction SilentlyContinue)) { New-Website -Name $siteName -PhysicalPath $physPath -Port 80 -HostHeader "localhost" -ApplicationPool $appPool | Out-Null }
# HTTPS binding (se já existir, ignora) try { if (-not (Get-WebBinding -Name $siteName -Protocol https -ErrorAction SilentlyContinue)) { # Tenta usar qualquer cert válido (ou deixe sem certificado se for instalar depois) $cert = Get-ChildItem Cert:\LocalMachine\My | Sort-Object NotAfter -Descending | Select-Object -First 1 if ($cert) { New-WebBinding -Name $siteName -Protocol https -BindingInformation $binding443 | Out-Null Push-Location IIS:\SslBindings Get-Item "0.0.0.0!443" -ErrorAction SilentlyContinue | Remove-Item -ErrorAction SilentlyContinue # Associa ao host 'localhost'; se usar FQDN, ajuste para ip!port!hostname New-Item "0.0.0.0!443" -Thumbprint $cert.Thumbprint -SSLFlags 0 | Out-Null Pop-Location } } } catch {}
# Security headers no web.config $webconfig = Join-Path $physPath "web.config" if (!(Test-Path $webconfig)) { @" <?xml version="1.0" encoding="utf-8"?> <configuration> <system.webServer> <httpProtocol> <customHeaders> <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" /> <add name="X-Content-Type-Options" value="nosniff" /> <add name="X-Frame-Options" value="SAMEORIGIN" /> <add name="Referrer-Policy" value="no-referrer" /> <add name="Content-Security-Policy" value="default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self' http://localhost:4900" /> </customHeaders> </httpProtocol> <staticContent> <remove fileExtension=".json" /> <mimeMap fileExtension=".json" mimeType="application/json" /> </staticContent> <directoryBrowse enabled="false" /> </system.webServer> </configuration> "@ | Out-File -FilePath $webconfig -Encoding UTF8 }
iisreset /restart | Out-Null Write-Host "[OK] Portal publicado: https://localhost/WAS-StatusPortal/" -ForegroundColor Green
Dica: Se você preferir publicar como aplicativo dentro do “Default Web Site” (ex.: /WAS-StatusPortal), troque New-Website por New-WebApplication e remova o binding dedicado.
⸻
3) Agendar atualização automática do status.json
Para que o painel sempre reflita o estado atual, agende a execução do coletor:
$ps = (Get-Command powershell.exe).Source $script = "C:\WAS\Bundle\Scripts\Generate-StatusJson.ps1"
$act = New-ScheduledTaskAction -Execute $ps -Argument "-NoProfile -ExecutionPolicy Bypass -File `"$script`"" $trg = New-ScheduledTaskTrigger -RepetitionInterval (New-TimeSpan -Minutes 1) -Once -At (Get-Date).AddMinutes(1) Register-ScheduledTask -TaskName "WAS_StatusJson_Update_1min" -Action $act -Trigger $trg -RunLevel Highest -Force | Out-Null Write-Host "[OK] Tarefa agendada para atualizar status.json a cada 1 min."
Pode mudar a periodicidade para 30s/2min/5min conforme a sua realidade.
⸻
4) Integrar ao Orchestrator (CLI)
Se quiser acionar a atualização junto da Auditoria ou em Publicar Portal, chame o coletor no fim dessas ações. Exemplo de linha que pode estar no seu WAS-Orchestrator.ps1:
# ao final do Publish-AuditReport ou Publish-AuditPortal: powershell -ExecutionPolicy Bypass -File "C:\WAS\Bundle\Scripts\Generate-StatusJson.ps1"
⸻
5) Troubleshooting rápido • Portal abre sem dados: • Verifique se existe C:\inetpub\WAS-StatusPortal\status.json e se é um JSON válido. • Rode manualmente o coletor:
powershell -File C:\WAS\Bundle\Scripts\Generate-StatusJson.ps1
• Sem HTTPS: • Confirme se há certificado em Cert:\LocalMachine\My. • Ajuste o binding (host/IP) no script de publicação. • Exporter indisponível: • Acesse http://localhost:4900/metrics. Se não abrir: • Inicie o exporter:
powershell -File C:\WAS\Bundle\Scripts\Metrics-Exporter.ps1
• Ou instale como serviço via NSSM.
• WebSockets 4900–4920 bloqueados: • Use Safe Test ON no Orchestrator, ou crie regras permanentes de firewall. • Permissões: • Garanta que o AppPool do portal tem permissão Read no C:\inetpub\WAS-StatusPortal.
⸻
Recap • index.html (UI) + status.json (dados) = WAS-StatusPortal. • Coletor Generate-StatusJson.ps1 mantém o JSON atualizado (tarefa a cada 1 min). • Publicação IIS com HTTPS + headers já deixa pronto para produção. • Exporter em 4900 é fallback e reforça a observabilidade.
index.html de exemplo para o WAS-StatusPortal. Ele: • carrega /status.json (gerado pelo orquestrador) e mostra: • sites do IIS, application pool, estado, bindings HTTP/HTTPS; • serviços WAS (wd*awp.exe); • certificados (subject, thumbprint, expiração); • último relatório de auditoria com link; • tem auto-refresh (30s) e indicadores coloridos (verde/âmbar/vermelho); • se não encontrar status.json, tenta um fallback simples lendo o exporter em http://localhost:4900/metrics para pelo menos exibir “IIS/WAS up/down”.
Coloque este arquivo em: C:\inetpub\WAS-StatusPortal\index.html (o menu do WAS-Orchestrator já cria o site e publica; este é um exemplo de front que você pode personalizar).
⸻
C:\inetpub\WAS-StatusPortal\index.html
//——
Ele inclui: • Layout responsivo, tema escuro claro (toggle). • Auto-refresh (30s) com botão “Atualizar agora”. • Leitura do status.json (com validações e mensagens claras). • Tabelas de Sites IIS, Serviços WAS (AWP) e Certificados com filtros rápidos. • Indicação de expiração de certificados (OK/WARN/FAIL). • Link automático para o WAS-Audit.html se publicado no portal. • Fallback para /metrics (caso status.json não exista). • Cabeçalho com ícones simples, status geral, e timestamp amigável.
Basta salvar o arquivo abaixo como C:\inetpub\WAS-StatusPortal\index.html. O status.json é gerado pelo script Generate-StatusJson.ps1 (como combinamos).
<!doctype html> <html lang="pt-br" data-theme="dark"> <head> <meta charset="utf-8"> <title>WAS Status Portal</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> <meta http-equiv="Cache-Control" content="no-store" /> <style> :root{ --bg:#0f1220; --panel:#151935; --panel-2:#101533; --ink:#e6e9ff; --muted:#9aa3c7; --ok:#27c93f; --warn:#ffbd2e; --fail:#ff5f56; --link:#7aa2ff; --line:#222a5a; --row:#1a1f42; --chip:#202657; --input:#0d1230; --kpi:#1b2050; } [data-theme="light"]{ --bg:#f7f9ff; --panel:#ffffff; --panel-2:#f1f4ff; --ink:#1a2142; --muted:#66709a; --ok:#1f9a33; --warn:#c08a0a; --fail:#d64545; --link:#2b5bd7; --line:#d7def7; --row:#eef2ff; --chip:#e9eeff; --input:#eef2ff; --kpi:#e9eeff; } *{box-sizing:border-box;font-family:Segoe UI, Roboto, Arial, sans-serif} body{margin:0;background:var(--bg);color:var(--ink)} header{position:sticky;top:0;z-index:10;background:linear-gradient(90deg,var(--panel),var(--panel-2));border-bottom:1px solid var(--line)} .hwrap{max-width:1200px;margin:0 auto;padding:14px 20px;display:flex;gap:14px;align-items:center;flex-wrap:wrap} .brand{display:flex;align-items:center;gap:10px} .logo{width:28px;height:28px;border-radius:6px;background:conic-gradient(from 180deg,#6b79ff,#00d0ff,#6b79ff)} h1{font-size:18px;margin:0} .spacer{flex:1} .btn{background:var(--panel-2);border:1px solid var(--line);color:var(--ink);padding:8px 12px;border-radius:8px;cursor:pointer;font-size:13px} .btn:hover{filter:brightness(1.05)} .chip{display:inline-flex;align-items:center;gap:6px;background:var(--chip);border:1px solid var(--line);padding:6px 10px;border-radius:14px;font-size:12px} .ok{color:var(--ok)} .warn{color:var(--warn)} .fail{color:var(--fail)} .muted{color:var(--muted)} .wrap{max-width:1200px;margin:0 auto;padding:20px} .grid{display:grid;grid-template-columns:repeat(12,1fr);gap:16px} .card{grid-column:span 12;background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px} .half{grid-column:span 12}@media(min-width:940px){.half{grid-column:span 6}} .third{grid-column:span 12}@media(min-width:940px){.third{grid-column:span 4}} h2{margin:0 0 10px 0;font-size:16px} .kpis{display:flex;gap:10px;flex-wrap:wrap} .kpi{background:var(--kpi);border:1px solid var(--line);padding:10px 12px;border-radius:10px} .kpi b{font-size:18px;margin-left:6px} table{width:100%;border-collapse:separate;border-spacing:0 6px} th,td{text-align:left;padding:10px 12px;font-size:13px} thead th{color:var(--muted)} tbody tr{background:var(--row);border:1px solid var(--line)} tbody td:first-child{border-radius:10px 0 0 10px} tbody td:last-child{border-radius:0 10px 10px 0} .status-dot{width:10px;height:10px;border-radius:50%;display:inline-block;margin-right:8px;vertical-align:middle} .sd-ok{background:var(--ok)} .sd-warn{background:var(--warn)} .sd-fail{background:var(--fail)} .pill{padding:2px 8px;border-radius:10px;border:1px solid var(--line);background:var(--panel-2);font-size:12px} .nowrap{white-space:nowrap} .row-actions{display:flex;gap:10px;flex-wrap:wrap} .searchbar{display:flex;gap:10px;flex-wrap:wrap;margin:8px 0 0 0} .input{background:var(--input);border:1px solid var(--line);color:var(--ink);padding:8px 10px;border-radius:8px;min-width:240px} .footer{color:var(--muted);font-size:12px;text-align:right;margin-top:6px} .help{font-size:12px;color:var(--muted)} a{color:var(--link);text-decoration:none} a:hover{text-decoration:underline} </style> </head> <body> <header> <div class="hwrap"> <div class="brand"> <div class="logo" aria-hidden="true"></div> <h1>WAS Status Portal</h1> </div> <div class="spacer"></div> <span id="overallState" class="chip"><span class="muted">Carregando…</span></span> <button id="refreshBtn" class="btn" type="button">Atualizar agora</button> <button id="themeBtn" class="btn" type="button">Tema: Escuro/Claro</button> </div> </header>
<div class="wrap"> <div class="grid"> <div class="card half"> <h2>Visão geral</h2> <div class="kpis" id="kpis"> <span class="kpi">Gerado: <b id="kpiGen">—</b></span> <span class="kpi">Sites: <b id="kpiSites">—</b></span> <span class="kpi">WAS (AWP): <b id="kpiWAS">—</b></span> <span class="kpi">Certificados: <b id="kpiCerts">—</b></span> </div> <div class="help" id="ageHint"></div> </div>
<div class="card half"> <h2>Relatórios</h2> <div id="reports"> <div class="help">Tentando localizar o relatório de auditoria…</div> </div> </div>
<div class="card"> <div style="display:flex;align-items:center;gap:10px;justify-content:space-between;flex-wrap:wrap"> <h2>Sites IIS</h2> <div class="searchbar"> <input id="siteFilter" class="input" placeholder="Filtrar por nome/host/porta…" /> </div> </div> <table> <thead> <tr><th>Site</th><th>Pool</th><th>Status</th><th>Bindings</th><th>HTTPS</th><th>Teste</th></tr> </thead> <tbody id="sitesBody"> <tr><td colspan="6" class="muted">Aguardando dados…</td></tr> </tbody> </table> </div>
<div class="card half"> <div style="display:flex;align-items:center;gap:10px;justify-content:space-between;flex-wrap:wrap"> <h2>Serviços WAS (AWP)</h2> <div class="searchbar"> <input id="wasFilter" class="input" placeholder="Filtrar por serviço/PID…" /> </div> </div> <table> <thead><tr><th>Serviço</th><th>Status</th><th>PID</th></tr></thead> <tbody id="wasBody"> <tr><td colspan="3" class="muted">Aguardando dados…</td></tr> </tbody> </table> </div>
<div class="card half"> <div style="display:flex;align-items:center;gap:10px;justify-content:space-between;flex-wrap:wrap"> <h2>Certificados HTTPS</h2> <div class="searchbar"> <input id="certFilter" class="input" placeholder="Filtrar por subject/thumbprint…" /> </div> </div> <table> <thead><tr><th>Subject</th><th>Thumbprint</th><th>Expira</th><th>Estado</th></tr></thead> <tbody id="certsBody"> <tr><td colspan="4" class="muted">Aguardando dados…</td></tr> </tbody> </table> <div class="help">Regra: <span class="ok">OK</span> ≥ 31 dias • <span class="warn">WARN</span> 1–30 dias • <span class="fail">FAIL</span> vencido</div> </div>
<div class="card third"> <h2>Exporter /metrics</h2> <div id="exporter" class="help">Testando http://localhost:4900/metrics…</div> </div>
<div class="card third"> <h2>Links úteis</h2> <ul style="margin:4px 0 0 16px"> <li><a href="/WAS-AuditPortal/WAS-Audit.html" target="_blank">Relatório de Auditoria (HTTPS)</a></li> <li><a href="/WAS-StatusPortal/status.json" target="_blank">status.json</a></li> <li><a href="http://localhost:4900/metrics" target="_blank">/metrics (Prometheus)</a></li> </ul> </div>
<div class="card third"> <h2>Sobre</h2> <div class="help">Gerado pelo WAS Orchestrator Kit v5.x • Atualiza a cada 30s</div> <div class="footer" id="footerTs"></div> </div> </div> </div>
<script> const REFRESH_MS = 30000; const S = (sel) => document.querySelector(sel); const esc = s => (s ?? '').toString().replace(/[&<>"']/g, m=>({ '&':'&','<':'<','>':'>','"':'"',"'":''' }[m])); const fmt = iso => { try{ return new Date(iso).toLocaleString(); } catch(e){ return iso||'—'; } }; function dot(state){ const cls = state==='OK'?'sd-ok':state==='WARN'?'sd-warn':'sd-fail'; return `<span class="status-dot ${cls}"></span>${state}`; } const pill = t => `<span class="pill">${esc(t)}</span>`; function ageHint(iso){ try{ const dt = new Date(iso); const mins = Math.max(0, Math.round((Date.now()-dt.getTime())/60000)); if (isNaN(mins)) return {label:'WARN', hint:'sem data'}; if (mins<10) return {label:'OK', hint:`há ${mins} min`}; if (mins<60) return {label:'WARN', hint:`há ${mins} min`}; return {label:'FAIL', hint:`há ${mins} min`}; }catch(e){ return {label:'WARN', hint:'sem data'} } }
let cacheData = null;
async function fetchJSON(url){ const r = await fetch(url, {cache:'no-store'}); if(!r.ok) throw new Error(`HTTP ${r.status}`); return r.json(); }
function overallState(data){ // heurística simples: se IIS up e pelo menos 1 site started → OK, senão FAIL; WARN se sem certs ou sem AWP const iisOk = !!data.iisUp; const sites = data.sites||[]; const started = sites.filter(s=> String(s.status).toLowerCase()==='started').length; const awp = (data.wasServices||[]).length; let label = 'OK'; if (!iisOk || started===0) label = 'FAIL'; else if (awp===0) label = 'WARN'; return label; }
function renderSummary(data){ const ts = data.generatedAt || data.timestamp; const age = ageHint(ts); S('#kpiGen').textContent = fmt(ts); S('#kpiSites').textContent = (data.sites||[]).length; S('#kpiWAS').textContent = (data.wasServices||[]).length; S('#kpiCerts').textContent = (data.certificates||[]).length; S('#ageHint').textContent = `Gerado ${age.hint}`; S('#footerTs').textContent = `Atualizado em ${fmt(ts)} • auto-refresh ${REFRESH_MS/1000}s`;
const label = overallState(data); const html = `<span class="status-dot ${label==='OK'?'sd-ok':label==='WARN'?'sd-warn':'sd-fail'}"></span> Estado geral: <b>${label}</b>`; S('#overallState').innerHTML = html; }
function siteRow(s){ const st = (s.status||'').toString().toUpperCase(); const label = st==='STARTED' ? 'OK' : (st==='STOPPED' ? 'FAIL' : 'WARN'); const https = (s.bindings||[]).filter(b=>b.protocol==='https'); const http = (s.bindings||[]).filter(b=>b.protocol==='http'); const links = [ ...https.map(b=> `https://${esc(b.host||'localhost')}:${esc(b.port||443)}`), ...http.map(b=> `http://${esc(b.host||'localhost')}:${esc(b.port||80)}`) ]; const testUrl = s.testUrl || links[0] || '#'; return `<tr> <td class="nowrap">${esc(s.name)}</td> <td class="nowrap">${esc(s.appPool||'-')}</td> <td>${dot(label)}</td> <td>${links.length? links.map(pill).join(' ') : '<span class="muted">—</span>'}</td> <td>${https.length? '<span class="ok">Sim</span>' : '<span class="warn">Não</span>'}</td> <td>${testUrl!=='#' ? `<a href="${esc(testUrl)}" target="_blank" rel="noopener">abrir</a>` : '<span class="muted">—</span>'}</td> </tr>`; }
function renderSites(data, filterText=''){ const tb = S('#sitesBody'); const all = (data.sites||[]); const f = filterText.trim().toLowerCase(); const list = !f ? all : all.filter(s=>{ const b = (s.bindings||[]).map(x=>`${x.protocol}://${x.host||'localhost'}:${x.port}`).join(' '); return String(s.name).toLowerCase().includes(f) || String(s.appPool||'').toLowerCase().includes(f) || b.toLowerCase().includes(f); }); if(!list.length){ tb.innerHTML = `<tr><td colspan="6" class="muted">Nenhum site encontrado</td></tr>`; return; } tb.innerHTML = list.map(siteRow).join(''); }
function renderWAS(data, filterText=''){ const tb = S('#wasBody'); const all = (data.wasServices||[]); const f = filterText.trim().toLowerCase(); const list = !f ? all : all.filter(x => String(x.name||'').toLowerCase().includes(f) || String(x.pid||'').toLowerCase().includes(f) ); if(!list.length){ tb.innerHTML = `<tr><td colspan="3" class="muted">Nenhum serviço encontrado</td></tr>`; return; } tb.innerHTML = list.map(s=>{ const st = (s.status||'').toString().toUpperCase(); const label = st==='RUNNING' ? 'OK' : (st==='STOPPED' ? 'FAIL' : 'WARN'); return `<tr> <td class="nowrap">${esc(s.name||'-')}</td> <td>${dot(label)}</td> <td class="nowrap">${esc(s.pid||'-')}</td> </tr>`; }).join(''); }
function renderCerts(data, filterText=''){ const tb = S('#certsBody'); const all = (data.certificates||[]); const f = filterText.trim().toLowerCase(); const list = !f ? all : all.filter(c => String(c.subject||'').toLowerCase().includes(f) || String(c.thumbprint||'').toLowerCase().includes(f) ); if(!list.length){ tb.innerHTML = `<tr><td colspan="4" class="muted">Nenhum certificado listado</td></tr>`; return; } tb.innerHTML = list.map(c=>{ const expISO = c.notAfter || c.expiresOn; const exp = new Date(expISO || Date.now()); const days = Math.round((exp.getTime() - Date.now())/86400000); let label = 'OK'; if (days<0) label='FAIL'; else if (days<=30) label='WARN'; return `<tr> <td class="nowrap">${esc(c.subject||'-')}</td> <td class="nowrap">${esc(c.thumbprint||'-')}</td> <td class="nowrap">${esc(expISO? fmt(expISO) : '—')} ${isFinite(days)? `(${days}d)` : ''}</td> <td>${dot(label)}</td> </tr>`; }).join(''); }
async function renderAuditLink(){ try{ const r = await fetch('/WAS-AuditPortal/WAS-Audit.html', {method:'HEAD'}); if (r.ok) S('#reports').innerHTML = `<a href="/WAS-AuditPortal/WAS-Audit.html" target="_blank" rel="noopener">Abrir relatório de auditoria (WAS-Audit.html)</a>`; else S('#reports').innerHTML = `<span class="help">Relatório ainda não publicado.</span>`; }catch(e){ S('#reports').innerHTML = `<span class="help">Relatório indisponível no momento.</span>`; } }
async function renderExporter(){ try{ const r = await fetch('http://localhost:4900/metrics', {cache:'no-store'}); const txt = await r.text(); const iis = /was_iis_status\s+([01])/m.exec(txt); const svcs = [...txt.matchAll(/was_service_status\{name="([^"]+)"\}\s+([01])/g)] .map(x=>({name:x[1], up:x[2]==='1'})); let html = ''; if (iis) html += `<div>${pill('IIS')} ${iis[1]==='1'?'<span class="ok">ON</span>':'<span class="fail">OFF</span>'}</div>`; if (svcs.length) html += `<div style="margin-top:6px">Serviços: ${svcs.map(s=>`${pill(esc(s.name))} ${s.up?'<span class="ok">ON</span>':'<span class="fail">OFF</span>'}`).join(' ')}</div>`; S('#exporter').innerHTML = html || '<span class="warn">Exporter acessível, mas sem métricas esperadas.</span>'; }catch(e){ S('#exporter').innerHTML = '<span class="fail">Exporter indisponível (http://localhost:4900/metrics)</span>'; } }
async function load(){ try{ const data = await fetchJSON('/WAS-StatusPortal/status.json'); cacheData = data; renderSummary(data); renderSites(data, S('#siteFilter').value||''); renderWAS(data, S('#wasFilter').value||''); renderCerts(data, S('#certFilter').value||''); }catch(e){ cacheData = { sites:[], wasServices:[], certificates:[], generatedAt:null, iisUp:false }; S('#overallState').innerHTML = `<span class="status-dot sd-warn"></span> status.json não encontrado`; S('#kpiGen').textContent='—'; S('#kpiSites').textContent='0'; S('#kpiWAS').textContent='0'; S('#kpiCerts').textContent='0'; S('#ageHint').textContent = 'Sem dados; verifique a tarefa de geração do status.json'; S('#sitesBody').innerHTML = `<tr><td colspan="6" class="muted">Sem status.json</td></tr>`; S('#wasBody').innerHTML = `<tr><td colspan="3" class="muted">Sem status.json</td></tr>`; S('#certsBody').innerHTML = `<tr><td colspan="4" class="muted">Sem status.json</td></tr>`; } await renderAuditLink(); await renderExporter(); }
// Filtros em tempo real S('#siteFilter').addEventListener('input', ()=> cacheData && renderSites(cacheData, S('#siteFilter').value)); S('#wasFilter').addEventListener('input', ()=> cacheData && renderWAS(cacheData, S('#wasFilter').value)); S('#certFilter').addEventListener('input', ()=> cacheData && renderCerts(cacheData, S('#certFilter').value));
// Botões S('#refreshBtn').addEventListener('click', load); S('#themeBtn').addEventListener('click', ()=>{ const root = document.documentElement; const cur = root.getAttribute('data-theme') || 'dark'; root.setAttribute('data-theme', cur==='dark' ? 'light' : 'dark'); });
// Auto-refresh load(); setInterval(load, REFRESH_MS); </script> </body> </html>
//——
aqui está um web.config enxuto e seguro para o WAS-StatusPortal, com: • Headers de segurança (HSTS, CSP, X-Frame-Options, etc.) • MIME para .json (exibe status.json corretamente) • Cache desativado apenas para status.json (evita “dados antigos” no painel) • Bloqueios de request filtering (impede servir .ps1, .bat, etc.) • Default document apontando para index.html • (Opcional) Redirect forçado para HTTPS (requer URL Rewrite)
Salve o WEB.config conteúdo abaixo em:
C:\inetpub\WAS-StatusPortal\web.config
<?xml version="1.0" encoding="utf-8"?> <configuration>
<!-- ===== Regras específicas para status.json (no-cache) ===== --> <location path="status.json"> <system.webServer> <httpProtocol> <customHeaders> <add name="Cache-Control" value="no-store, no-cache, must-revalidate, max-age=0" /> <add name="Pragma" value="no-cache" /> <add name="Expires" value="0" /> </customHeaders> </httpProtocol> </system.webServer> </location>
<system.webServer>
<!-- ===== Documentos, navegação e MIME ===== --> <defaultDocument> <files> <clear /> <add value="index.html" /> </files> </defaultDocument>
<directoryBrowse enabled="false" />
<staticContent> <!-- JSON e mapas de sourcemap (se usar assets compilados) --> <remove fileExtension=".json" /> <mimeMap fileExtension=".json" mimeType="application/json" /> <remove fileExtension=".map" /> <mimeMap fileExtension=".map" mimeType="application/octet-stream" /> </staticContent>
<!-- ===== Headers de segurança (ajuste a CSP conforme seus assets) ===== --> <httpProtocol> <customHeaders> <!-- HTTPS estrito por 1 ano (inclui subdomínios e pré-carga) --> <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains; preload" /> <!-- Proteções básicas --> <add name="X-Content-Type-Options" value="nosniff" /> <add name="X-Frame-Options" value="SAMEORIGIN" /> <add name="Referrer-Policy" value="no-referrer" /> <add name="Permissions-Policy" value="geolocation=(), microphone=(), camera=()" /> <!-- CSP: libere apenas o necessário; conectamos ao exporter 4900 para fallback --> <add name="Content-Security-Policy" value="default-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'; connect-src 'self' http://localhost:4900" /> </customHeaders> </httpProtocol>
<!-- ===== Request Filtering (endurecimento) ===== --> <security> <requestFiltering> <!-- Oculta segmentos sensíveis --> <hiddenSegments> <add segment="bin" /> </hiddenSegments> <!-- Bloqueia extensões potencialmente perigosas --> <fileExtensions> <add fileExtension=".ps1" allowed="false" /> <add fileExtension=".psm1" allowed="false" /> <add fileExtension=".psd1" allowed="false" /> <add fileExtension=".cmd" allowed="false" /> <add fileExtension=".bat" allowed="false" /> <add fileExtension=".reg" allowed="false" /> <add fileExtension=".config" allowed="false" /> </fileExtensions> <!-- Limites conservadores (ajuste conforme necessidade) --> <requestLimits maxAllowedContentLength="10485760" /> <!-- 10 MB --> </requestFiltering> </security>
<!-- (Opcional) Compressão estática --> <httpCompression> <dynamicTypes> <add enabled="false" mimeType="*/*" /> </dynamicTypes> <staticTypes> <add enabled="true" mimeType="text/*" /> <add enabled="true" mimeType="application/javascript" /> <add enabled="true" mimeType="application/json" /> <add enabled="true" mimeType="application/xml" /> </staticTypes> </httpCompression>
<!-- (Opcional) Redirect forçado para HTTPS — requer URL Rewrite instalado Para usar, remova os comentários abaixo. <rewrite> <rules> <rule name="Force HTTPS" enabled="true" stopProcessing="true"> <match url="(.*)" ignoreCase="true" /> <conditions> <add input="{HTTPS}" pattern="off" ignoreCase="true" /> </conditions> <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" /> </rule> </rules> </rewrite> -->
</system.webServer> </configuration>
//——
Teste rápido 1. Reinicie o IIS:
iisreset /restart
2. Acesse:
https://localhost/WAS-StatusPortal/ https://localhost/WAS-StatusPortal/status.json
Se status.json estiver “grudando” em cache, já deixamos Cache-Control no-store só para ele. Para o resto do portal, o cache segue normal (útil para performance).
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 24,septiembre 2025 - 05:28 |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 24,septiembre 2025 - 13:36 |
# Manual de Uso do WAS Orchestrator Kit v5.1
**Autor:** Manus AI
## Índice
1. [Finalidade](#finalidade) 2. [Visão Geral do Kit](#visão-geral-do-kit) * [Estrutura de Arquivos](#estrutura-de-arquivos) * [Componentes Principais](#componentes-principais) 3. [Descrição Detalhada dos Scripts](#descrição-detalhada-dos-scripts) * [`WAS-Orchestrator.ps1`](#was-orchestratorps1) * [`Metrics-Exporter.ps1`](#metrics-exporterps1) * [`Metrics-Exporter-Service.ps1`](#metrics-exporter-serviceps1) * [`Metrics-Exporter-Wrapper.ps1`](#metrics-exporter-wrapperps1) * [`DR-Plan.ps1`](#dr-planps1) * [`Sign-All.ps1`](#sign-allps1) * [`Validate-Install.ps1`](#validate-installps1) * [`Load-Config.ps1`](#load-configps1) * [`Orchestrator-API.ps1`](#orchestrator-apips1) * [`Cert-Watch.ps1`](#cert-watchps1) 4. [Exemplos de Uso](#exemplos-de-uso) * [Executando o Menu Principal](#executando-o-menu-principal) * [Instalando o Metrics Exporter como Serviço](#instalando-o-metrics-exporter-como-serviço) * [Executando um Plano de DR](#executando-um-plano-de-dr) * [Validando a Integridade do Kit](#validando-a-integridade-do-kit) * [Gerando Inventário de Certificados](#gerando-inventário-de-certificados) 5. [Conclusão](#conclusão)
## Finalidade
O WAS Orchestrator Kit v5.1 é um conjunto de scripts PowerShell projetado para simplificar e automatizar a manutenção, monitoramento e gerenciamento de ambientes que utilizam o Microsoft Internet Information Services (IIS) e o Windows Activation Service (WAS). Em ambientes de produção, a estabilidade e a segurança desses serviços são cruciais. Este kit visa fornecer aos administradores de sistema e engenheiros de DevOps um conjunto de ferramentas robustas para garantir a operação contínua, a conformidade de segurança e a capacidade de resposta a incidentes, tudo isso através de uma interface de linha de comando ou via API. Ele consolida diversas tarefas rotineiras e complexas em scripts fáceis de usar, reduzindo a chance de erros manuais e otimizando o tempo de gerenciamento. A capacidade de exportar métricas, validar a integridade da instalação e gerenciar certificados são exemplos de como o kit aborda as necessidades críticas de um ambiente de servidor moderno. Além disso, a inclusão de um plano de recuperação de desastres (DR-Plan) e funcionalidades de endurecimento (hardening) demonstra uma abordagem proativa para a resiliência e segurança do sistema. O kit é uma solução abrangente para o gerenciamento eficiente e seguro de infraestruturas baseadas em IIS e WAS, permitindo que as equipes se concentrem em tarefas de maior valor agregado, enquanto as operações de rotina são automatizadas e monitoradas de forma eficaz.
## Visão Geral do Kit
O WAS Orchestrator Kit v5.1 é estruturado de forma modular para facilitar o gerenciamento e a extensão. Ele agrupa scripts, configurações e documentação em diretórios lógicos, permitindo que os administradores naveguem e utilizem as ferramentas de forma eficiente. A arquitetura do kit reflete uma abordagem prática para o gerenciamento de sistemas, onde diferentes aspectos da manutenção e monitoramento são tratados por componentes especializados, mas interconectados. Essa modularidade não apenas melhora a organização, mas também permite que cada script seja focado em uma tarefa específica, tornando o kit mais robusto e fácil de depurar. A inclusão de arquivos de configuração externos, como YAML, demonstra uma preocupação com a flexibilidade e a capacidade de adaptação a diferentes ambientes sem a necessidade de modificar o código-fonte dos scripts. Além disso, a presença de um catálogo de integridade e scripts de assinatura de código ressalta a importância da segurança e da confiança na execução desses utilitários em ambientes de produção. A seguir, detalhamos a estrutura de arquivos e os componentes principais que compõem este kit.
### Estrutura de Arquivos
O kit é organizado em um diretório `Bundle` com as seguintes subpastas:
* **`Bundle/Scripts`**: Este diretório é o coração do kit, contendo todos os scripts PowerShell que realizam as operações de gerenciamento, monitoramento e automação. Cada script é projetado para uma funcionalidade específica, promovendo a modularidade e a reutilização de código. Exemplos incluem scripts para iniciar/parar serviços, exportar métricas, gerenciar certificados e executar planos de recuperação de desastres. * **`Bundle/Config`**: Armazena arquivos de configuração em formato YAML, como `orchestrator.yaml`. Esses arquivos são utilizados pelos scripts para carregar parâmetros e configurações específicas do ambiente, permitindo uma personalização fácil sem a necessidade de alterar o código dos scripts. Isso é particularmente útil para adaptar o kit a diferentes cenários de implantação. * **`Bundle/Profiles`**: Contém perfis e definições para funcionalidades específicas, como o `DR-Plan.yaml`, que descreve os passos e recursos envolvidos em um plano de recuperação de desastres. A separação dos perfis de configuração dos scripts principais permite que os administradores definam e gerenciem diferentes estratégias de DR de forma independente. * **`Bundle/Docs`**: Inclui a documentação básica do kit, como o `README-v5.1.txt`. Embora este manual forneça uma visão mais aprofundada, o README original oferece um ponto de partida rápido para entender o propósito e as funcionalidades básicas do kit. * **`Bundle/Catalog`**: Contém o arquivo `WAS-CATALOG-SHA256.txt`, que é um catálogo de hashes SHA256 de todos os arquivos do kit. Este arquivo é crucial para a funcionalidade de validação de integridade, garantindo que os scripts e outros componentes não foram adulterados ou corrompidos.
### Componentes Principais
Os principais componentes do kit são os scripts PowerShell, cada um com uma função específica:
* **`WAS-Orchestrator.ps1`**: O script principal que atua como um menu interativo, centralizando o acesso a diversas funcionalidades do kit. * **`Metrics-Exporter.ps1`**: Exporta métricas de desempenho e status do IIS/WAS/TLS no formato Prometheus, facilitando a integração com sistemas de monitoramento. * **`Metrics-Exporter-Service.ps1`**: Gerencia a instalação e o ciclo de vida do `Metrics-Exporter.ps1` como um serviço Windows, garantindo sua execução contínua. * **`Metrics-Exporter-Wrapper.ps1`**: Um script que monitora e reinicia o `Metrics-Exporter.ps1` caso ele pare, assegurando a disponibilidade da exportação de métricas. * **`DR-Plan.ps1`**: Executa planos de recuperação de desastres definidos em arquivos YAML, automatizando a resposta a falhas. * **`Sign-All.ps1`**: Assina digitalmente todos os scripts PowerShell do kit, aumentando a segurança e a conformidade em ambientes restritivos. * **`Validate-Install.ps1`**: Verifica a integridade dos arquivos do kit usando hashes SHA256, detectando qualquer alteração ou corrupção. * **`Load-Config.ps1`**: Um utilitário para carregar configurações de arquivos YAML, usado por outros scripts para obter parâmetros de execução. * **`Orchestrator-API.ps1`**: Expõe uma API HTTP local para permitir a interação programática com as funcionalidades do Orchestrator, facilitando a automação e integração. * **`Cert-Watch.ps1`**: Gera um inventário de certificados SSL/TLS instalados, monitorando suas datas de expiração e alertando sobre certificados próximos ao vencimento.
## Descrição Detalhada dos Scripts
Esta seção oferece uma análise aprofundada de cada script PowerShell incluído no WAS Orchestrator Kit v5.1, detalhando suas funcionalidades, parâmetros e o papel que desempenham no ecossistema de gerenciamento de IIS e WAS. Compreender o propósito e a operação de cada script é fundamental para utilizar o kit de forma eficaz e para solucionar problemas que possam surgir. Cada script foi projetado com um objetivo específico em mente, contribuindo para a completude e a versatilidade do kit. A interconexão entre alguns desses scripts, como o `WAS-Orchestrator.ps1` que invoca outros scripts, demonstra uma arquitetura bem pensada para centralizar o controle e a execução de tarefas. Além disso, a inclusão de mecanismos de logging e tratamento de erros em muitos dos scripts reflete uma preocupação com a robustez e a capacidade de auditoria das operações realizadas. A seguir, exploraremos cada script individualmente, fornecendo informações essenciais para sua compreensão e uso.
### `WAS-Orchestrator.ps1`
O `WAS-Orchestrator.ps1` é o ponto de entrada principal para a maioria das operações manuais do kit. Ele apresenta um menu interativo que permite ao usuário selecionar diversas ações relacionadas ao gerenciamento do IIS e WAS. Este script é ideal para administradores que preferem uma interface baseada em menu para executar tarefas comuns ou para explorar as capacidades do kit. Ele encapsula a lógica de chamada para outros scripts e funções, tornando a experiência do usuário mais coesa e simplificada. A estrutura do menu é intuitiva, guiando o usuário através das opções disponíveis e fornecendo feedback sobre o sucesso ou falha das operações. A capacidade de iniciar e parar serviços, aplicar configurações de segurança e executar diagnósticos a partir de um único ponto centralizado é um dos grandes benefícios deste script.
**Funcionalidades Principais:**
* **Iniciar/Parar IIS**: Controla o serviço `W3SVC` (World Wide Web Publishing Service). * **Iniciar/Parar WAS**: Controla os serviços `WD*AWP` (Windows Process Activation Service). * **Safe Test Mode**: Um recurso para abrir portas de firewall específicas (4900, 4901, 4902, 4910, 4920) e desativar o firewall temporariamente para facilitar testes. Inclui uma opção para reverter essas configurações. * **Hardening Max**: Aplica configurações de segurança a um pool de aplicativos IIS (`WebdevAppPool`), garantindo que ele esteja sempre ativo (`AlwaysRunning`), sem tempo limite de inatividade (`idleTimeout`) ou reinícios periódicos (`recycling.periodicRestart.time`). * **Diagnósticos**: Realiza verificações de status para o IIS e WAS, e lista o estado de todos os sites configurados no IIS. * **Executar DR-Plan**: Invoca o script `DR-Plan.ps1` para iniciar um plano de recuperação de desastres. * **Validação de Integridade**: Invoca o script `Validate-Install.ps1` para verificar se os arquivos do kit foram alterados. * **Status do Exporter**: Verifica o status do serviço `WASExporter`, que é gerenciado pelo `Metrics-Exporter-Service.ps1`.
**Exemplo de Uso (interativo):**
```powershell .\WAS-Orchestrator.ps1 ```
Ao executar, um menu será exibido, e o usuário poderá digitar o número da opção desejada.
### `Metrics-Exporter.ps1`
O `Metrics-Exporter.ps1` é um componente vital para o monitoramento de ambientes IIS/WAS, projetado para expor métricas no formato compatível com Prometheus. Ele opera como um servidor HTTP leve, ouvindo em uma porta específica (padrão: 4900) e respondendo a requisições `/metrics` com dados de telemetria. A capacidade de integrar essas métricas com sistemas de monitoramento modernos, como Prometheus e Grafana, permite a criação de dashboards detalhados e alertas proativos sobre a saúde e o desempenho dos serviços. Este script coleta informações cruciais sobre o estado dos serviços, a configuração de sites e a segurança de protocolos TLS, fornecendo uma visão abrangente do ambiente.
**Métricas Exportadas:**
* **`was_iis_status`**: Status do serviço IIS (1 para ativo, 0 para parado). * **`was_service_status{name="<nome_do_serviço_WAS>"}`**: Status de cada serviço WAS individualmente. * **`was_sites_total`**: Número total de sites IIS configurados. * **`was_site_status{name="<nome_do_site>"}`**: Status de cada site IIS (1 para iniciado, 0 para parado). * **`was_https_bindings_total`**: Número total de bindings HTTPS configurados. * **`was_tls10_enabled`, `was_tls11_enabled`, `was_tls12_enabled`, `was_tls13_enabled`**: Indica se as versões específicas do TLS estão habilitadas no servidor (1 para habilitado, 0 para desabilitado), lendo diretamente do registro do Windows.
**Endpoint Padrão:** `http://localhost:4900/metrics`
**Exemplo de Uso (execução direta):**
```powershell .\Metrics-Exporter.ps1 ```
Este script geralmente é executado como um serviço em segundo plano, gerenciado pelo `Metrics-Exporter-Service.ps1` ou monitorado pelo `Metrics-Exporter-Wrapper.ps1`.
### `Metrics-Exporter-Service.ps1`
Para garantir a execução contínua e confiável do `Metrics-Exporter.ps1`, o `Metrics-Exporter-Service.ps1` oferece funcionalidades para instalar, gerenciar e remover o exportador como um serviço Windows. Ele utiliza o NSSM (Non-Sucking Service Manager), uma ferramenta de terceiros, para transformar o script PowerShell em um serviço persistente. Isso é fundamental para ambientes de produção, onde a disponibilidade do exportador de métricas é crítica para o monitoramento ininterrupto. O script simplifica o processo de configuração do serviço, abstraindo a complexidade da interação direta com o NSSM.
**Ações Suportadas:**
* **`Install`**: Instala o `Metrics-Exporter.ps1` como um serviço Windows (`WASExporter`) e o inicia. Requer que o NSSM esteja disponível no caminho `C:\nssm\nssm.exe`. * **`Uninstall`**: Para e remove o serviço `WASExporter`. * **`Start`**: Inicia o serviço `WASExporter`. * **`Stop`**: Para o serviço `WASExporter`. * **`Restart`**: Reinicia o serviço `WASExporter`. * **`Status`**: Exibe o status atual do serviço `WASExporter`.
**Parâmetros:**
* `-Action <Install|Uninstall|Start|Stop|Restart|Status>`: Ação a ser executada no serviço.
**Exemplo de Uso (instalar o serviço):**
```powershell .\Metrics-Exporter-Service.ps1 -Action Install ```
### `Metrics-Exporter-Wrapper.ps1`
O `Metrics-Exporter-Wrapper.ps1` atua como um guardião para o `Metrics-Exporter.ps1`. Em cenários onde o exportador de métricas não é executado como um serviço Windows (ou mesmo como uma camada adicional de resiliência), este wrapper garante que o `Metrics-Exporter.ps1` esteja sempre em execução. Ele monitora ativamente o processo do exportador e, caso detecte que ele parou, tenta reiniciá-lo automaticamente. Isso é particularmente útil para manter a continuidade da coleta de métricas sem intervenção manual, aumentando a robustez do sistema de monitoramento. Todas as ações do wrapper são logadas em um arquivo `Metrics-Exporter-Wrapper.log` para auditoria e depuração.
**Funcionalidade:**
* Monitora o processo do `Metrics-Exporter.ps1`. * Reinicia o `Metrics-Exporter.ps1` automaticamente se ele não estiver em execução. * Registra todas as ações e eventos em um arquivo de log.
**Exemplo de Uso:**
```powershell .\Metrics-Exporter-Wrapper.ps1 ```
Este script é projetado para ser executado continuamente em segundo plano.
### `DR-Plan.ps1`
O `DR-Plan.ps1` é um script crucial para a resiliência do ambiente, permitindo a execução automatizada de um plano de recuperação de desastres. Ele lê as instruções de um arquivo YAML (`DR-Plan.yaml`) e executa ações predefinidas, como iniciar sites críticos e parar outros sites não essenciais, além de reiniciar pools de aplicativos em uma ordem específica. A automação do DR-Plan reduz significativamente o tempo de recuperação (RTO) e minimiza o impacto de falhas, garantindo que os serviços mais importantes sejam restaurados rapidamente. A dependência de um arquivo YAML externo para a configuração do plano oferece flexibilidade, permitindo que diferentes planos de DR sejam definidos e executados conforme a necessidade.
**Funcionalidades:**
* Lê o plano de DR de um arquivo YAML (padrão: `..\Profiles\DR-Plan.yaml`). * Inicia um site IIS crítico especificado no plano. * Opcionalmente, para todos os outros sites IIS que não são o site crítico. * Reinicia pools de aplicativos em uma ordem definida no arquivo YAML. * Registra todas as ações em um arquivo `DR-Plan.log`.
**Parâmetros:**
* `-PlanFile <caminho_para_yaml>`: Caminho opcional para o arquivo YAML do plano de DR.
**Pré-requisitos:**
* PowerShell 7+ é recomendado para a função `ConvertFrom-Yaml`. O script inclui um fallback básico, mas a funcionalidade completa pode exigir a versão mais recente do PowerShell.
**Exemplo de Uso:**
```powershell .\DR-Plan.ps1 ```
Ou com um arquivo de plano específico:
```powershell .\DR-Plan.ps1 -PlanFile "C:\MyDRPlans\MyCriticalDR.yaml" ```
### `Sign-All.ps1`
Em ambientes corporativos com políticas de segurança rigorosas, a execução de scripts PowerShell não assinados pode ser bloqueada. O `Sign-All.ps1` resolve esse problema, permitindo que todos os scripts do kit sejam assinados digitalmente usando um certificado Authenticode. Isso garante a autenticidade e a integridade dos scripts, permitindo sua execução em sistemas com políticas de execução restritivas (por exemplo, `AllSigned` ou `RemoteSigned`). A assinatura de código é uma prática de segurança essencial que ajuda a proteger contra a execução de scripts maliciosos ou adulterados, fornecendo uma camada de confiança para os administradores.
**Funcionalidades:**
* Localiza todos os arquivos `.ps1` dentro do `BundlePath` (padrão: `C:\WAS\Bundle`). * Assina os scripts com um certificado Authenticode especificado. * Permite a inclusão de um servidor de carimbo de data/hora (timestamp server) para garantir a validade da assinatura mesmo após a expiração do certificado. * Pode forçar a re-assinatura de scripts já assinados. * Registra o processo de assinatura em um arquivo de log.
**Parâmetros:**
* `-BundlePath <caminho>`: Caminho para o diretório raiz do kit (padrão: `C:\WAS\Bundle`). * `-StoreLocation <CurrentUser|LocalMachine>`: Localização do armazenamento de certificados (padrão: `LocalMachine`). * `-StoreName <nome_do_store>`: Nome do armazenamento de certificados (padrão: `My`). * `-Thumbprint <thumbprint_do_certificado>`: O thumbprint do certificado de assinatura de código a ser usado. Se não for fornecido, o script tentará selecionar o certificado mais recente de "Code Signing". * `-TimestampServer <url_do_servidor>`: URL de um servidor de carimbo de data/hora. * `-Force`: Força a re-assinatura de scripts já assinados.
**Exemplo de Uso (assinando com um thumbprint específico):**
```powershell .\Sign-All.ps1 -Thumbprint "SEU_THUMBPRINT_DO_CERTIFICADO" -TimestampServer "http://timestamp.digicert.com" ```
### `Validate-Install.ps1`
O `Validate-Install.ps1` é uma ferramenta de auditoria e segurança que verifica a integridade da instalação do WAS Orchestrator Kit. Ele compara os hashes SHA256 de todos os arquivos do kit com um catálogo de hashes pré-calculados (`WAS-CATALOG-SHA256.txt`). Essa validação é crucial para detectar qualquer modificação não autorizada, corrupção de arquivos ou infecção por malware. Em ambientes de alta segurança, a verificação regular da integridade dos arquivos é uma prática recomendada para garantir que as ferramentas de gerenciamento permaneçam confiáveis. O script pode gerar um relatório HTML detalhado, facilitando a visualização e o compartilhamento dos resultados da auditoria.
**Funcionalidades:**
* Calcula o hash SHA256 de cada arquivo do kit. * Compara os hashes calculados com os hashes listados no `WAS-CATALOG-SHA256.txt`. * Identifica arquivos com hashes diferentes (`Mismatch`), arquivos ausentes (`Missing`) e arquivos extras (`Extra`). * Gera um relatório HTML com os resultados da validação, incluindo detalhes sobre quaisquer inconsistências. * Registra todas as ações em um arquivo de log.
**Parâmetros:**
* `-BundlePath <caminho>`: Caminho para o diretório raiz do kit (padrão: `C:\WAS\Bundle`). * `-Catalog <caminho>`: Caminho para o arquivo de catálogo SHA256 (padrão: `C:\WAS\Bundle\Catalog\WAS-CATALOG-SHA256.txt`). * `-Exclude <padrões>`: Padrões de exclusão para arquivos que não devem ser validados (por exemplo, arquivos de log ou o próprio catálogo). * `-HtmlReport <caminho_do_relatório>`: Caminho para salvar o relatório HTML de validação. * `-Quiet`: Suprime a saída para o console, apenas registrando no log.
**Exemplo de Uso (gerando relatório HTML):**
```powershell .\Validate-Install.ps1 -HtmlReport "C:\Reports\IntegrityReport.html" ```
### `Load-Config.ps1`
O `Load-Config.ps1` é um script utilitário interno, projetado para carregar configurações de arquivos YAML. Ele é utilizado por outros scripts do kit, como `DR-Plan.ps1` e `Orchestrator-API.ps1`, para obter parâmetros de configuração de forma estruturada. A utilização de arquivos YAML para configuração externa promove a flexibilidade, permitindo que os administradores ajustem o comportamento dos scripts sem modificar seu código-fonte. O script é inteligente o suficiente para detectar se o PowerShell 7+ está disponível (que inclui o cmdlet `ConvertFrom-Yaml`) e, caso contrário, oferece um fallback básico para parsing de YAML, garantindo compatibilidade com versões mais antigas do PowerShell, embora com suporte limitado a estruturas YAML complexas.
**Funcionalidades:**
* Lê e faz o parse de arquivos de configuração YAML. * Suporta `ConvertFrom-Yaml` no PowerShell 7+. * Inclui um fallback básico para parsing de YAML em versões anteriores do PowerShell.
**Parâmetros:**
* `-ConfigPath <caminho_para_yaml>`: Caminho para o arquivo de configuração YAML (padrão: `..\Config\orchestrator.yaml`).
**Exemplo de Uso (interno, geralmente chamado por outros scripts):**
```powershell $config = .\Load-Config.ps1 -ConfigPath "..\Config\orchestrator.yaml" ```
### `Orchestrator-API.ps1`
O `Orchestrator-API.ps1` estende as capacidades do kit, expondo uma API HTTP local que permite a interação programática com as funcionalidades do Orchestrator. Isso é particularmente útil para integração com sistemas de automação externos, ferramentas de orquestração ou scripts personalizados que precisam acionar ações no ambiente IIS/WAS sem a necessidade de interação manual com o menu do `WAS-Orchestrator.ps1`. A API oferece endpoints para verificar o status dos serviços, iniciar/parar componentes e executar planos de DR ou auditorias, transformando o kit em um recurso programável.
**Endpoints da API:**
* **`/status`**: Retorna o status atual dos serviços IIS e WAS em formato JSON. * **`/start-iis`**: Inicia o serviço IIS. * **`/stop-iis`**: Para o serviço IIS. * **`/start-was`**: Inicia os serviços WAS. * **`/stop-was`**: Para os serviços WAS. * **`/dr`**: Executa o `DR-Plan.ps1`. * **`/audit`**: Executa o `Validate-Install.ps1`.
**Porta Padrão:** Definida no arquivo `orchestrator.yaml` (ex: 4900).
**Exemplo de Uso (execução direta para iniciar a API):**
```powershell .\Orchestrator-API.ps1 ```
Após a execução, a API estará disponível em `http://localhost:<porta_da_api>/`.
### `Cert-Watch.ps1`
O `Cert-Watch.ps1` é uma ferramenta essencial para a gestão proativa de certificados SSL/TLS. Ele escaneia os armazenamentos de certificados configurados no sistema, coleta informações sobre cada certificado (assunto, thumbprint, datas de validade) e calcula os dias restantes para a expiração. Com base em limites configuráveis (dias para aviso e dias para crítico), o script classifica a severidade de cada certificado e gera um relatório HTML detalhado. Este relatório é inestimável para evitar interrupções de serviço causadas por certificados expirados, permitindo que os administradores renovem os certificados com antecedência. A capacidade de gerar um relatório visualmente amigável torna a auditoria de certificados uma tarefa muito mais simples e eficiente.
**Funcionalidades:**
* Escaneia armazenamentos de certificados (configuráveis via `orchestrator.yaml`). * Coleta detalhes de certificados, incluindo datas de validade. * Calcula os dias restantes para a expiração. * Classifica a severidade (OK, Aviso, Crítico) com base em limites configuráveis. * Gera um relatório HTML com o inventário de certificados, incluindo formatação visual para severidade.
**Parâmetros:**
* `-ConfigPath <caminho_para_yaml>`: Caminho opcional para o arquivo de configuração YAML (padrão: `..\Config\orchestrator.yaml`).
**Configuração (via `orchestrator.yaml`):**
```yaml certwatch: warn_days: 30 crit_days: 7 html_report: "C:\Reports\CertInventory.html" stores: - location: LocalMachine name: My - location: CurrentUser name: My ```
**Exemplo de Uso:**
```powershell .\Cert-Watch.ps1 ```
Este script gerará um arquivo HTML no caminho especificado na configuração, que pode ser aberto em um navegador para visualização.
## Exemplos de Uso
Esta seção fornece exemplos práticos de como utilizar os scripts do WAS Orchestrator Kit v5.1. Os exemplos são projetados para ilustrar as operações mais comuns e demonstrar a flexibilidade e o poder do kit na automação de tarefas de gerenciamento e monitoramento de IIS/WAS. É importante notar que, para a execução de muitos desses scripts, é necessário ter privilégios de administrador no sistema Windows onde o IIS e o WAS estão instalados. Além disso, a política de execução do PowerShell pode precisar ser ajustada (por exemplo, para `RemoteSigned` ou `Bypass`) para permitir a execução de scripts baixados da internet. Recomenda-se sempre revisar o código dos scripts antes da execução em um ambiente de produção. Os exemplos a seguir cobrem desde a interação com o menu principal até a instalação de serviços e a geração de relatórios, fornecendo um guia passo a passo para as funcionalidades chave do kit.
### Executando o Menu Principal
O script `WAS-Orchestrator.ps1` oferece uma interface de menu interativa para acessar rapidamente as principais funcionalidades do kit. É a maneira mais simples de começar a usar o kit para tarefas manuais.
1. **Abra o PowerShell como Administrador.** 2. **Navegue até o diretório `Bundle/Scripts`** onde o kit foi extraído:
```powershell cd C:\caminho\para\WAS_Orchestrator_Kit_v5.1_FULL\Bundle\Scripts ```
3. **Execute o script principal:**
```powershell .\WAS-Orchestrator.ps1 ```
4. Um menu será exibido, permitindo que você escolha entre opções como iniciar/parar serviços, executar diagnósticos ou ativar o modo de teste seguro. Digite o número correspondente à opção desejada e pressione Enter.
``` === WAS Orchestrator v5.1 === 1. Iniciar IIS 2. Parar IIS 3. Iniciar WAS 4. Parar WAS 5. Safe Test Mode 6. Hardening Max 7. Diagnósticos 8. Executar DR-Plan 9. Validação de Integridade 10. Status do Exporter 0. Sair Escolha: _ ```
### Instalando o Metrics Exporter como Serviço
Para garantir que as métricas do IIS/WAS sejam exportadas continuamente, é recomendável instalar o `Metrics-Exporter.ps1` como um serviço Windows usando o `Metrics-Exporter-Service.ps1`. Este processo requer o NSSM (Non-Sucking Service Manager).
1. **Certifique-se de que o NSSM esteja disponível.** Por padrão, o script espera que o `nssm.exe` esteja em `C:\nssm\nssm.exe`. Se não estiver, você precisará baixá-lo e colocá-lo nesse local ou modificar o script `Metrics-Exporter-Service.ps1` para apontar para o caminho correto. 2. **Abra o PowerShell como Administrador.** 3. **Navegue até o diretório `Bundle/Scripts`**:
```powershell cd C:\caminho\para\WAS_Orchestrator_Kit_v5.1_FULL\Bundle\Scripts ```
4. **Instale o serviço:**
```powershell .\Metrics-Exporter-Service.ps1 -Action Install ```
5. Após a instalação, o serviço `WAS Metrics Exporter Service` será iniciado automaticamente. Você pode verificar seu status através do Gerenciador de Serviços do Windows ou usando o próprio script:
```powershell .\Metrics-Exporter-Service.ps1 -Action Status ```
6. Para desinstalar o serviço, use:
```powershell .\Metrics-Exporter-Service.ps1 -Action Uninstall ```
### Executando um Plano de DR
O `DR-Plan.ps1` permite automatizar a execução de um plano de recuperação de desastres definido em um arquivo YAML. Este exemplo assume que você tem um `DR-Plan.yaml` configurado no diretório `Bundle/Profiles`.
1. **Abra o PowerShell como Administrador.** 2. **Navegue até o diretório `Bundle/Scripts`**:
```powershell cd C:\caminho\para\WAS_Orchestrator_Kit_v5.1_FULL\Bundle\Scripts ```
3. **Execute o script `DR-Plan.ps1`:**
```powershell .\DR-Plan.ps1 ```
O script lerá o `DR-Plan.yaml` padrão e executará as ações definidas (iniciar site crítico, parar outros sites, reiniciar pools de aplicativos).
4. Você pode especificar um arquivo de plano diferente, se necessário:
```powershell .\DR-Plan.ps1 -PlanFile "C:\MyCustomPlans\EmergencyDR.yaml" ```
### Validando a Integridade do Kit
Para garantir que os arquivos do kit não foram adulterados, o `Validate-Install.ps1` pode ser usado para verificar sua integridade. Ele pode gerar um relatório HTML para fácil visualização.
1. **Abra o PowerShell como Administrador.** 2. **Navegue até o diretório `Bundle/Scripts`**:
```powershell cd C:\caminho\para\WAS_Orchestrator_Kit_v5.1_FULL\Bundle\Scripts ```
3. **Execute o script `Validate-Install.ps1` para gerar um relatório HTML:**
```powershell .\Validate-Install.ps1 -HtmlReport "C:\Reports\IntegrityReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').html" ```
4. Um arquivo HTML será gerado no caminho especificado (neste caso, `C:\Reports\`). Abra este arquivo em um navegador para visualizar o relatório de integridade, que detalhará quaisquer arquivos ausentes, extras ou com hashes inconsistentes.
### Gerando Inventário de Certificados
O `Cert-Watch.ps1` é útil para monitorar a validade dos certificados SSL/TLS instalados no sistema, gerando um relatório HTML que destaca certificados próximos da expiração.
1. **Abra o PowerShell como Administrador.** 2. **Navegue até o diretório `Bundle/Scripts`**:
```powershell cd C:\caminho\para\WAS_Orchestrator_Kit_v5.1_FULL\Bundle\Scripts ```
3. **Execute o script `Cert-Watch.ps1`:**
```powershell .\Cert-Watch.ps1 ```
4. O script gerará um arquivo HTML (caminho configurado em `orchestrator.yaml`, por padrão `C:\Reports\CertInventory.html`) com uma tabela listando todos os certificados encontrados, suas datas de validade e a severidade (OK, Aviso, Crítico) com base nos dias restantes para expiração. Abra este arquivo em um navegador para revisar o inventário.
## Conclusão
O WAS Orchestrator Kit v5.1 representa uma solução abrangente e bem estruturada para o gerenciamento e monitoramento de ambientes IIS e WAS. Através de uma coleção de scripts PowerShell modulares, o kit aborda uma vasta gama de necessidades operacionais, desde o controle básico de serviços até funcionalidades avançadas como exportação de métricas para sistemas de monitoramento, automação de planos de recuperação de desastres, validação de integridade de arquivos e gerenciamento proativo de certificados SSL/TLS. A análise estática dos scripts revelou uma arquitetura robusta, com clara separação de responsabilidades e a inclusão de boas práticas de segurança, como a assinatura de código e a verificação de integridade. A disponibilidade de uma API HTTP local expande ainda mais as possibilidades de automação e integração com outras ferramentas de orquestração, tornando o kit uma peça valiosa em qualquer pipeline de DevOps ou estratégia de gerenciamento de infraestrutura. Embora a dependência de ferramentas externas como NSSM e a preferência por PowerShell 7+ para certas funcionalidades sejam pontos a serem considerados, o kit oferece soluções eficazes para mitigar esses aspectos, como o fallback básico para parsing de YAML. Em suma, o WAS Orchestrator Kit v5.1 é uma ferramenta poderosa que capacita administradores e engenheiros a manterem seus ambientes IIS/WAS seguros, eficientes e resilientes, minimizando a intervenção manual e otimizando a resposta a eventos críticos. Sua completude e a profundidade de suas funcionalidades o tornam um recurso indispensável para qualquer equipe que gerencie esses serviços em escala.
## Complementaridade com o Script `Setup-WAS-IIS.ps1`
É importante notar a existência de scripts complementares que, embora não façam parte do WAS Orchestrator Kit v5.1, desempenham um papel crucial na configuração inicial de ambientes IIS/WAS, especialmente para o WebDev Application Server. Um exemplo notável é o script `Setup-WAS-IIS.ps1`, discutido em um fórum da PC SOFT [1]. Este script tem uma finalidade distinta do kit de orquestração, focando na instalação e configuração inicial do ambiente, enquanto o kit se concentra na manutenção e monitoramento contínuos.
### `Setup-WAS-IIS.ps1` (Script de Instalação e Configuração)
O `Setup-WAS-IIS.ps1` é um script PowerShell projetado para automatizar a configuração completa do IIS para o WebDev Application Server. Ele aborda os desafios comuns encontrados durante a instalação e garante que o ambiente esteja otimizado para o funcionamento do WebDev. Suas funcionalidades são essenciais para estabelecer uma base sólida antes que as ferramentas de manutenção e orquestração do kit possam ser plenamente utilizadas.
**Funcionalidades Principais:**
* **Instalação de Recursos do IIS**: Garante que todos os componentes necessários do IIS para o WebDev/WAS estejam instalados e configurados corretamente. * **Criação e Configuração de Usuário Local**: Opcionalmente, cria um usuário local dedicado (`WebdevAdmin`) e o associa aos grupos de segurança apropriados (`Administrators` e `IIS_IUSRS`), seguindo as melhores práticas de segurança e isolamento. * **Configuração de Application Pool**: Cria e configura Application Pools com identidades específicas, otimizando o desempenho e a segurança das aplicações WebDev. * **Registro de ISAPI/Handlers**: Configura os handlers e módulos ISAPI necessários para que o IIS processe corretamente as extensões de arquivo do WebDev (`.awp`, `.awws`, `.wb`). * **Permissões NTFS**: Aplica as permissões de sistema de arquivos (NTFS) adequadas nas pastas do IIS e do PC SOFT, resolvendo problemas comuns de acesso. * **Correções para DISM**: Inclui lógica para corrigir problemas relacionados ao Deployment Image Servicing and Management (DISM) que podem impactar a funcionalidade do WAS em certos ambientes. * **Criação e Teste de Aplicação Virtual**: Automatiza a criação de aplicações virtuais no IIS e realiza testes de URL para validar a configuração e acessibilidade. * **Diagnóstico Abrangente**: Oferece um diagnóstico detalhado do ambiente, fornecendo um resumo percentual da prontidão do servidor para o WebDev/WAS.
### Relação com o WAS Orchestrator Kit v5.1
É crucial entender que o `Setup-WAS-IIS.ps1` e o WAS Orchestrator Kit v5.1 são **complementares**, e não substitutos um do outro. O `Setup-WAS-IIS.ps1` atua como uma ferramenta de **provisionamento e configuração inicial**, garantindo que o ambiente IIS/WAS esteja corretamente instalado e otimizado para o WebDev. Uma vez que essa base é estabelecida, o WAS Orchestrator Kit v5.1 assume o papel de **gerenciamento contínuo**, oferecendo as ferramentas necessárias para monitorar, manter, proteger e garantir a resiliência do ambiente ao longo do tempo.
Em um ciclo de vida típico de um servidor, o `Setup-WAS-IIS.ps1` seria executado uma vez (ou sempre que uma reconfiguração completa fosse necessária) para preparar o ambiente. Posteriormente, o WAS Orchestrator Kit v5.1 seria empregado diariamente para operações como monitoramento de métricas, execução de planos de DR, validação de integridade e gerenciamento de certificados. Juntos, eles fornecem uma solução completa para o ciclo de vida de ambientes IIS/WAS com WebDev.
**Referência:**
[1] Boller, A. J. (2025, 22 de setembro). *SUPER SCRIPT PARA VALIDAR W.A.S. E Ms Internet Information Server (Msiis)*. Fóruns desenvolvedores PC SOFT. Disponível em: [https://forum.pcsoft.fr/fr-FR/pcsoft.br.windev/5311-super-script-para-validar-internet-information-server-msiis/read.awp](https://forum.pcsoft.fr/fr-FR/pcsoft.br.windev/5311-super-script-para-validar-internet-information-server-msiis/read.awp)
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | |
| |
Miembro registrado 4.613 mensajes |
|
| Publicado el 24,septiembre 2025 - 13:38 |
# Análise de Completude e Funcionalidades do WAS Orchestrator Kit v5.1
Com base na análise estática dos scripts PowerShell e do arquivo README, o kit WAS Orchestrator v5.1 oferece um conjunto robusto de ferramentas para a manutenção e monitoramento de ambientes IIS (Msiis) e WAS (Windows Activation Service).
## Estrutura do Kit
O kit é organizado da seguinte forma:
* **Bundle/Scripts**: Contém todos os scripts PowerShell principais. * **Bundle/Config**: Contém arquivos de configuração, como `orchestrator.yaml`. * **Bundle/Profiles**: Contém perfis para planos de DR, como `DR-Plan.yaml`. * **Bundle/Docs**: Contém documentação básica, como `README-v5.1.txt`. * **Bundle/Catalog**: Contém o catálogo SHA256 para validação de integridade, `WAS-CATALOG-SHA256.txt`.
## Análise de Funcionalidades por Script
### `WAS-Orchestrator.ps1`
Este é o script principal, funcionando como um menu interativo para gerenciar as operações. Suas funcionalidades incluem:
* **Controle de Serviços**: Iniciar e parar os serviços IIS (W3SVC) e WAS (serviços `WD*AWP`). * **Safe Test Mode**: Um modo para abrir portas específicas e desativar o firewall temporariamente para testes, com opção de reversão. * **Hardening Max**: Configurações de endurecimento para um pool de aplicativos IIS (`WebdevAppPool`), garantindo que ele esteja sempre em execução, sem tempo limite de inatividade ou reinício periódico. * **Diagnósticos**: Verifica o status dos serviços IIS e WAS, e lista o estado de todos os sites IIS. * **Execução de DR-Plan**: Invoca o script `DR-Plan.ps1` para executar um plano de recuperação de desastres. * **Validação de Integridade**: Invoca o script `Validate-Install.ps1` para verificar a integridade dos arquivos do kit. * **Status do Exporter**: Verifica o status do serviço `WASExporter`.
### `Metrics-Exporter.ps1`
Este script atua como um exportador de métricas no formato Prometheus para monitoramento. Ele expõe um endpoint HTTP (`http://localhost:4900/metrics`) que fornece:
* Status do serviço IIS. * Status dos serviços WAS. * Contagem total de sites IIS e status individual de cada site. * Contagem de bindings HTTPS. * Status de ativação de protocolos TLS (1.0, 1.1, 1.2, 1.3) via registro do Windows.
### `Metrics-Exporter-Service.ps1`
Gerencia a instalação e o ciclo de vida do `Metrics-Exporter.ps1` como um serviço Windows, utilizando o NSSM (Non-Sucking Service Manager). As ações suportadas são:
* Instalar o serviço. * Desinstalar o serviço. * Iniciar o serviço. * Parar o serviço. * Reiniciar o serviço. * Verificar o status do serviço.
### `Metrics-Exporter-Wrapper.ps1`
Um script wrapper que monitora a execução do `Metrics-Exporter.ps1` e o reinicia automaticamente caso ele pare. Isso garante a continuidade da exportação de métricas.
### `DR-Plan.ps1`
Executa um plano de recuperação de desastres definido em um arquivo YAML (`DR-Plan.yaml`). As funcionalidades incluem:
* Inicia um site IIS crítico especificado no plano. * Opcionalmente, para todos os outros sites IIS. * Reinicia pools de aplicativos específicos em uma ordem definida.
### `Sign-All.ps1`
Assina digitalmente todos os scripts PowerShell (`.ps1`) do kit usando um certificado Authenticode. Isso é crucial para ambientes com políticas de execução de PowerShell restritivas, garantindo a confiança nos scripts.
### `Validate-Install.ps1`
Valida a integridade dos arquivos do kit comparando seus hashes SHA256 com um catálogo (`WAS-CATALOG-SHA256.txt`). Ele pode:
* Identificar arquivos com hashes diferentes (mismatch). * Identificar arquivos ausentes. * Identificar arquivos extras. * Gerar um relatório HTML detalhado sobre a validação.
### `Load-Config.ps1`
Um script utilitário para carregar configurações de arquivos YAML, como `orchestrator.yaml`. Ele suporta PowerShell 7+ para `ConvertFrom-Yaml` e inclui um fallback para versões anteriores.
### `Orchestrator-API.ps1`
Expõe uma API HTTP local para interagir com as funcionalidades do Orchestrator. Isso permite a automação e integração com outras ferramentas. As rotas incluem:
* `/status`: Retorna o status do IIS e WAS. * `/start-iis`, `/stop-iis`, `/start-was`, `/stop-was`: Controla os serviços IIS e WAS. * `/dr`: Executa o plano de DR. * `/audit`: Executa a validação de instalação.
### `Cert-Watch.ps1`
Gera um inventário de certificados SSL/TLS instalados no sistema, verificando sua validade e emitindo alertas (crítico, aviso, ok) com base nos dias restantes para expiração. Ele pode gerar um relatório HTML.
## Completude e Avaliação Geral
O kit parece ser **muito completo** para a finalidade proposta de manutenção e monitoramento de ambientes IIS/WAS. Ele aborda diversas áreas críticas:
* **Operações Básicas**: Iniciar/parar serviços. * **Segurança**: Hardening, assinatura de scripts. * **Monitoramento**: Exportação de métricas para Prometheus, inventário de certificados. * **Resiliência**: Plano de recuperação de desastres. * **Automação**: API HTTP para integração. * **Integridade**: Validação de instalação.
A inclusão de um wrapper para o exportador de métricas e a capacidade de assinar scripts são pontos fortes que indicam uma preocupação com a robustez e segurança do ambiente. A dependência de `ConvertFrom-Yaml` para PowerShell 7+ em alguns scripts é uma consideração importante, mas o `Load-Config.ps1` oferece um fallback básico.
**Pontos Fortes:**
* **Abrangente**: Cobre um amplo espectro de tarefas de manutenção e monitoramento. * **Modular**: Scripts bem separados por funcionalidade. * **Automatizável**: A API e os scripts de serviço permitem automação. * **Seguro**: Assinatura de código e validação de integridade são boas práticas. * **Monitorável**: Exportação de métricas e inventário de certificados são essenciais para ambientes de produção.
**Possíveis Melhorias/Considerações (sem execução):**
* **Documentação Detalhada**: Embora haja um README, um manual mais aprofundado com exemplos e pré-requisitos seria benéfico (o que será gerado). * **Gerenciamento de Dependências**: A dependência do NSSM para o serviço do exporter e do PowerShell 7+ para `ConvertFrom-Yaml` deve ser claramente documentada. * **Configuração Centralizada**: O uso de arquivos YAML para configuração é bom, mas a forma como esses arquivos são gerenciados e distribuídos em um ambiente de produção pode ser explorada.
Em resumo, o kit é uma solução bem pensada e completa para o gerenciamento de ambientes IIS/WAS, oferecendo ferramentas essenciais para operação, segurança e monitoramento.
## Análise do Script Complementar: `Setup-WAS-IIS.ps1`
O script `Setup-WAS-IIS.ps1`, encontrado no fórum da PC SOFT [1], difere significativamente do WAS Orchestrator Kit v5.1 em sua finalidade principal. Enquanto o kit original foca na **manutenção, monitoramento e orquestração** de ambientes IIS/WAS já existentes, o `Setup-WAS-IIS.ps1` é um script de **instalação e configuração completa** do IIS especificamente para o WebDev Application Server (WAS) da PC SOFT. Ele é projetado para preparar um servidor Windows do zero ou para corrigir configurações problemáticas relacionadas à integração do WebDev com o IIS.
**Funcionalidades Principais do `Setup-WAS-IIS.ps1`:**
* **Instalação de Recursos do IIS**: Garante que todos os recursos necessários do IIS para o WebDev/WAS estejam instalados. * **(Opcional) Criação de Usuário Local Dedicado**: Cria um usuário local (`WebdevAdmin` por padrão) e o adiciona aos grupos `Administrators` e `IIS_IUSRS`, o que é uma prática comum para isolamento de privilégios. * **Configuração de Application Pool**: Cria e configura um Application Pool com identidade específica. * **Configuração de ISAPI/Handlers**: Registra os handlers (`.awp`, `.awws`, `.wb`) e o ISAPI do `WDxxAWP.exe` para o WebDev. * **Permissões NTFS**: Aplica as permissões de sistema de arquivos necessárias nas pastas do IIS/PC SOFT. * **(Opcional) Correções para DISM**: Tenta corrigir problemas de acesso ao DISM que podem afetar o funcionamento do WAS em alguns ambientes. * **Criação e Teste de Aplicação Virtual**: Cria uma aplicação virtual no IIS e testa sua URL para verificar a configuração. * **Diagnóstico Abrangente**: Executa um diagnóstico detalhado com um resumo percentual da prontidão do ambiente. * **"Guard Rails" e Idempotência**: O script é projetado para ser idempotente (pode ser executado múltiplas vezes sem efeitos colaterais indesejados) e inclui "guard rails" (mecanismos de segurança) para evitar ações arriscadas, a menos que explicitamente habilitadas por switches.
**Diferenças e Complementaridade:**
Característica / Script | WAS Orchestrator Kit v5.1 | `Setup-WAS-IIS.ps1` (do fórum) | :---------------------------- | :--------------------------------------------------------- | :----------------------------------------------------------- | **Finalidade Principal** | Manutenção, monitoramento, orquestração de ambientes existentes. | Instalação e configuração inicial do IIS para WebDev/WAS. | **Foco** | Operações contínuas, segurança, resiliência, automação. | Preparação do ambiente, correção de problemas de instalação. | **Recursos Chave** | Menu interativo, exportação de métricas, DR-Plan, validação de integridade, API, monitoramento de certificados. | Instalação de componentes IIS, criação de usuário, configuração de handlers, permissões, diagnóstico de prontidão. | **Público-alvo** | Administradores de sistema, equipes de DevOps. | Desenvolvedores WebDev, administradores que configuram novos servidores ou solucionam problemas de instalação. |
**Conclusão sobre a Complementaridade:**
O `Setup-WAS-IIS.ps1` é um excelente script para a fase inicial de **configuração e validação de um ambiente IIS/WAS para WebDev**. Ele garante que a base esteja corretamente estabelecida. Uma vez que o ambiente esteja configurado e funcionando com sucesso por meio deste script, o **WAS Orchestrator Kit v5.1 entra em cena para gerenciar e monitorar esse ambiente em sua operação diária**. Ou seja, os dois scripts são complementares: o `Setup-WAS-IIS.ps1` prepara o terreno, e o WAS Orchestrator Kit v5.1 mantém o jardim. Não há sobreposição direta de funcionalidades, mas sim uma sequência lógica de uso para um ciclo de vida completo de um ambiente IIS/WAS com WebDev.
**Referência:**
[1] Boller, A. J. (2025, 22 de setembro). *SUPER SCRIPT PARA VALIDAR W.A.S. E Ms Internet Information Server (Msiis)*. Fóruns desenvolvedores PC SOFT. Disponível em: [https://forum.pcsoft.fr/fr-FR/pcsoft.br.windev/5311-super-script-para-validar-internet-information-server-msiis/read.awp](https://forum.pcsoft.fr/fr-FR/pcsoft.br.windev/5311-super-script-para-validar-internet-information-server-msiis/read.awp)
-- Adriano José Boller ______________________________________________ Consultor e Representante Oficial da PcSoft no Brasil +55 (41) 99949 1800 adrianoboller@gmail.com skype: adrianoboller http://wxinformatica.com.br/ |
| |
| |
| | | |
|
| | | | |
| | |
| | |
| |
|
|
|