Windows Server 2025 / 관리 스크립트에 사전 확인·오류 처리·실행 로그를 넣는 방법
관리 스크립트는 명령이 끝났다는 사실과 작업이 성공했다는 사실을 구분해야 합니다. PowerShell의 try·catch·finally와 명시적인 오류 처리, 실행별 로그를 사용하면 실패 원인을 남기고 일부 작업만 수행된 상황도 더 정확하게 판단할 수 있습니다.
실패를 숨기지 않는 구조를 만듭니다
이 글은 서버를 변경하지 않는 파일 조회 예제로 오류 처리부터 익힙니다. 실제 역할 설치나 계정 생성 스크립트에서는 중간 실패 뒤 어떤 변경이 남는지도 기록해야 합니다. catch가 실행된다고 이전 변경이 자동 취소되는 것은 아닙니다.
PowerShell에는 명령이 오류를 표시한 뒤 다음 줄로 진행하는 경우가 있습니다. try 안에 넣는 것만으로 모든 오류를 잡을 수 없으며, 필요한 명령에 -ErrorAction Stop을 지정해 처리 대상으로 만들 수 있습니다. 구체적인 동작은 PowerShell 오류 처리 설명에서 확인할 수 있습니다.
성공과 실패를 비교할 실습 파일을 준비합니다
C:\LabScripts에 UTF-8 텍스트 파일 input.txt를 만들고 lab ready 한 줄을 넣습니다. 같은 폴더에 아래 전체 예제를 Read-LabFile.ps1로 저장합니다. Windows PowerShell 5.1에서 실행하며 서버 역할이나 네트워크는 변경하지 않습니다.
param(
[Parameter(Mandatory=$true)]
[string]$InputPath
)
$logDirectory = 'C:\LabLogs'
New-Item -ItemType Directory -Path $logDirectory -Force -ErrorAction Stop | Out-Null
$runLabel = (Get-Date -Format 'yyyyMMdd-HHmmss') + '-' + ([guid]::NewGuid().ToString('N').Substring(0,6))
$logPath = Join-Path $logDirectory ($runLabel + '.json')
$result = [ordered]@{
StartedAt = (Get-Date).ToString('o')
ComputerName = $env:COMPUTERNAME
InputPath = $InputPath
Success = $false
LineCount = $null
ErrorId = $null
ErrorMessage = $null
}
$exitStatus = 0
try {
$lines = @(Get-Content -LiteralPath $InputPath -ErrorAction Stop)
$result.LineCount = $lines.Count
$result.Success = $true
}
catch {
$result.ErrorId = $_.FullyQualifiedErrorId
$result.ErrorMessage = $_.Exception.Message
$exitStatus = 1
Write-Warning $result.ErrorMessage
}
finally {
$result.FinishedAt = (Get-Date).ToString('o')
$result | ConvertTo-Json | Set-Content -LiteralPath $logPath -Encoding UTF8 -ErrorAction Stop
}
Write-Output $logPath
exit $exitStatus
같은 조건에서 성공과 실패를 비교합니다
첫 명령은 존재하는 input.txt를 읽습니다. 두 번째 명령은 이 실습 폴더에 존재하지 않는 missing-input.txt를 지정합니다. 먼저 같은 이름의 파일이 정말 없는지 확인하세요. 파일 내용을 지우거나 시스템 파일을 변경할 필요는 없습니다.
powershell.exe -NoProfile -File 'C:\LabScripts\Read-LabFile.ps1' -InputPath 'C:\LabScripts\input.txt' $LASTEXITCODE powershell.exe -NoProfile -File 'C:\LabScripts\Read-LabFile.ps1' -InputPath 'C:\LabScripts\missing-input.txt' $LASTEXITCODE
각 실행 직후 종료 코드를 확인합니다. 성공 예시에서는 0과 Success=true, 없는 파일 예시에서는 1과 Success=false 및 오류 정보가 기대 결과입니다. 두 번의 실행은 서로 다른 JSON 파일을 만들며, 로그 경로를 열어 InputPath와 ErrorId를 비교합니다. 실행 정책이 차단하면 조직 정책과 서명 요건을 확인하고, 예제를 실행하려고 전역 정책을 완화하지 않습니다.
try·catch·finally의 역할을 확인합니다
| 구역 | 이 예제에서 하는 일 |
|---|---|
| try | 대상 파일을 읽고 행 수를 기록합니다. |
| catch | 처리한 오류의 식별자와 메시지를 남기고 실패 종료 코드를 정합니다. |
| finally | 읽기 성공 여부와 관계없이 실행 결과를 저장하려고 시도합니다. |
try·catch·finally 공식 설명에 따라 catch는 처리 대상이 되는 종료 오류를 받습니다. finally가 있어도 로그 디스크가 가득 차거나 폴더 접근이 거부되면 저장이 실패할 수 있습니다. 그래서 로그 저장에도 -ErrorAction Stop을 사용하고, 로그가 없는데 성공했다고 판단하지 않습니다.
외부 프로그램은 별도의 종료 코드를 반환합니다. Windows PowerShell 5.1에서 외부 명령의 실패가 자동으로 catch를 실행한다고 가정하지 말고, 실행 직후 $LASTEXITCODE를 확인해 필요하면 throw로 처리합니다. 다른 외부 명령을 실행하면 값이 바뀔 수 있습니다.
실무 스크립트에는 재실행 기준을 추가합니다
- 서버 이름과 시작·종료 시각, 작업 대상을 실행별로 기록합니다.
- 이미 적용된 설정이 있으면 현재 상태를 조회해 중복 생성과 덮어쓰기를 피합니다.
- 성공한 단계와 실패한 단계를 나누어 남깁니다.
- 암호·토큰·사용자 파일 본문처럼 불필요한 비밀정보는 로그에 넣지 않습니다.
- 자동 복구를 넣는다면 되돌릴 수 있는 범위와 복구 실패도 따로 기록합니다.
실습 비교를 마치면 입력 경로를 정상 파일로 되돌려 다시 실행하고, 성공 코드와 새 결과 파일을 확인하세요. 임시 입력과 로그는 보존할 결과를 확인한 뒤 이 실습에서 만든 파일만 정리합니다. 오류를 감추는 SilentlyContinue를 모든 명령에 붙이는 방식으로 성공률을 높인 것처럼 만들지 않습니다.









