「지난주 금요일 밤중에 서버가 멋대로 재시작된 것 같다」「특정 단말에서만 업무 앱이 한 달에 몇 번씩 다운된다」── 이런 조사의 출발점은 거의 반드시 Windows의 이벤트 로그입니다. 그러나 이벤트 뷰어의 GUI로 수십만 건의 로그를 스크롤하다 보면, 그것만으로 오전이 끝나버립니다.
PowerShell의 Get-WinEvent를 사용하면 이 작업은 수십 초 만에 끝납니다. 단, 조건이 있습니다. 필터링을 올바른 위치에서 수행하는 것입니다. Get-WinEvent | Where-Object { ... }라고 작성해 버리면 모든 이벤트를 읽어들인 후에 버리게 되어, GUI보다 느려지는 경우조차 있습니다.
이 글에서는 Get-WinEvent의 필터링을 올바르게 사용하는 방법과, 현장에서 자주 나오는 조사(예기치 않은 재시작, 로그온, 앱의 비정상 종료, 서비스 중지)의 레시피, 그리고 여러 대에서의 수집까지 정리합니다.
전제 환경
| 항목 | 내용 |
|---|---|
| 대상 OS | Windows 10/11, Windows Server. Get-WinEvent가 읽는 것은 Windows Vista 이후의 이벤트 로그 기반으로, 클래식 로그와 ETW 로그를 모두 다룰 수 있습니다1 |
| PowerShell 버전 | Windows PowerShell 5.1・PowerShell 7 어느 쪽이든 사용할 수 있습니다(Get-WinEvent는 Windows 전용 명령어로, Linux/macOS의 PowerShell 7에서는 사용할 수 없습니다). 이 글의 코드는 5.1과 7 양쪽에서 동작하는 방식으로 작성했습니다1 |
| 권한 | System・Application 등 다수의 로그는 일반 사용자도 읽을 수 있지만, Security 로그를 읽으려면 관리자 권한이나 이를 대신하는 권한 부여가 필요합니다. 관리자 권한으로 실행하지 않으면 정보를 얻을 수 없는 로그가 있습니다(4장(2)에 권한 부여 절차가 있습니다)12 |
| 원격 취득 | 5장의 방법 1(-ComputerName)은 PowerShell 원격 처리(WinRM)에 의존하지 않습니다. 대신 대상 머신의 방화벽에서 이벤트 로그 서비스로의 원격 액세스를 허용해 두어야 합니다. 방법 2(Invoke-Command)는 WinRM이 전제입니다1 |
1. 먼저 결론
Get-EventLog가 아니라Get-WinEvent를 사용합니다. 전자는 Windows PowerShell 전용이며 클래식 로그만 다루고, PowerShell 7에서는 사용할 수 없습니다.1- 필터링은
-FilterHashtable로 수행합니다. 이벤트 로그 쪽에서 필터링되므로,Where-Object에 의한 후단 필터링보다 차원이 다르게 빨라집니다.3 -FilterHashtable의 키는 정해져 있습니다.LogNameProviderNameIDLevelStartTimeEndTimeKeywordsPathUserID등입니다.3- 복잡한 조건이나 이벤트 데이터로 필터링할 때는 XPath(
-FilterXPath)입니다. 이벤트 뷰어의 「사용자 지정 보기」에서 XPath를 복사할 수 있습니다.1 Level은 숫자입니다. 1=심각, 2=오류, 3=경고, 4=정보, 5=자세한 정보.4- 보안 로그를 읽으려면 특별한 권한이 필요합니다. 관리자 권한으로 실행하는 것이 간편하지만, 조사 담당자에게는 「Event Log Readers」 그룹이나 채널 ACL로 읽기만 부여하는 편이 최소 권한 원칙에 부합합니다.12
- 메시지 문자열이 아니라 이벤트 데이터를 봅니다.
ToXml()로 구조화된 데이터를 얻으면 OS의 언어 설정에 의존하지 않는 스크립트가 됩니다.1 .evtx파일도 분석할 수 있습니다.-Path를 지정하면 현지에서 채취한 로그를 로컬에서 조사할 수 있습니다.1- 상시 집약에는 Windows 이벤트 전달(WEF)을 사용합니다. 단발성 조사라면
Invoke-Command에 의한 병렬 실행으로 충분합니다.5
2. 두 종류의 로그와 명령어 선택
Windows의 이벤트 로그에는 크게 2개 계통이 있습니다.
| 종류 | 예 | 읽을 수 있는 명령어 |
|---|---|---|
| 클래식 로그 | System / Application / Security | Get-EventLog(5.1 전용)・Get-WinEvent |
| 애플리케이션 및 서비스 로그 | Microsoft-Windows-TaskScheduler/Operational 등 |
Get-WinEvent만 |
후자에야말로 조사에 유용한 정보가 들어 있습니다. 작업 스케줄러의 실행 이력, PowerShell의 스크립트 블록 로그, Windows 업데이트의 적용 이력 등, 원인 규명의 결정적 단서가 되는 로그의 대부분은 이쪽에 있습니다. 따라서 앞으로 작성하는 스크립트는 Get-WinEvent로 통일하는 것이 정답입니다.1
먼저 어떤 로그가 있는지 확인합니다.
# 로그 목록(건수가 많은 순). RecordCount가 0인 로그는 기록되지 않음
Get-WinEvent -ListLog * -ErrorAction SilentlyContinue |
Where-Object RecordCount -gt 0 |
Sort-Object RecordCount -Descending |
Select-Object LogName, RecordCount, MaximumSizeInBytes, IsEnabled -First 20
# 특정 제품의 로그를 찾는다
Get-WinEvent -ListLog *TaskScheduler* | Format-Table LogName, IsEnabled, RecordCount
Get-WinEvent -ListProvider *PowerShell* | Select-Object Name
3. 필터링은 「어디서 하는가」가 전부다
같은 결과를 얻는 3가지 작성 방식을 비교합니다.
# 【최악】전체를 읽어들인 후 PowerShell 쪽에서 버린다
Get-WinEvent -LogName System | Where-Object { $_.Id -eq 41 }
# 【권장】이벤트 로그 쪽에서 좁힌다(FilterHashtable)
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ID = 41 }
# 【복잡한 조건】XPath로 좁힌다
Get-WinEvent -LogName System -FilterXPath "*[System[EventID=41]]"
첫 번째는 수십만 건의 이벤트를 모두 객체화한 후 버리기 때문에 대기 시간의 대부분이 낭비됩니다. 두 번째・세 번째는 이벤트 로그의 API 쪽에서 필터링하므로 필요한 것만 반환됩니다.3
-FilterHashtable에서 사용할 수 있는 키는 정해져 있습니다.3
| 키 | 지정하는 것 | 예 |
|---|---|---|
LogName |
로그 이름 | 'System', 'Microsoft-Windows-TaskScheduler/Operational' |
ProviderName |
이벤트 발생원 | 'Application Error', 'Service Control Manager' |
ID |
이벤트 ID(배열 가능) | 41, @(1000, 1001) |
Level |
심각도(숫자) | 2(오류), @(1,2) |
StartTime / EndTime |
기간 | (Get-Date).AddDays(-7) |
Keywords |
키워드(감사 성공/실패 등) | 9007199254740992(감사 성공) |
Path |
.evtx 파일 |
'D:\collect\srv01_System.evtx' |
UserID |
사용자 SID | 'S-1-5-21-...' |
Level의 숫자는 다음과 같습니다.4
값(Level) |
한국어 환경 표시 | 영어 환경 표시 |
|---|---|---|
| 1 | 심각 | Critical |
| 2 | 오류 | Error |
| 3 | 경고 | Warning |
| 4 | 정보 | Information |
| 5 | 자세한 정보 | Verbose |
오른쪽 2열은 이벤트 뷰어의 「수준」 열과 Get-WinEvent 결과의 LevelDisplayName 속성에 나오는 문자열입니다. LevelDisplayName은 OS의 표시 언어에 따라 달라집니다. 출력을 눈으로 대조할 때는 이 대응표를 사용하고, 스크립트로 필터링할 때는 반드시 숫자인 Level을 지정하십시오(표시 이름으로 판정하면 영어 환경에서 동작하지 않는 스크립트가 됩니다).
실용적인 형태로 만들면 이렇게 됩니다.
# 최근 7일간의 오류・심각 이벤트를 발생원별로 집계한다(상황 파악의 첫걸음)
$filter = @{
LogName = 'System', 'Application'
Level = 1, 2
StartTime = (Get-Date).AddDays(-7)
}
try {
Get-WinEvent -FilterHashtable $filter -ErrorAction Stop |
Group-Object ProviderName, Id |
Sort-Object Count -Descending |
Select-Object Count, Name -First 15
}
catch {
# 「해당 이벤트 없음」만 조용히 넘긴다. 로캘에 의존하지 않도록
# FullyQualifiedErrorId로 판정한다(메시지 문자열은 한국어 환경에서 일치하지 않음)
if ($_.FullyQualifiedErrorId -notlike 'NoMatchingEventsFound*') { throw }
}
결과를 읽는 법. Group-Object의 출력은 Count와 Name 2개 열입니다. ProviderName, Id처럼 여러 속성으로 그룹화하고 있으므로, Name에는 발생원, 이벤트 ID가 쉼표로 구분되어 나열됩니다(예: Service Control Manager, 7034). 건수가 많은 순으로 정렬되어 있으므로, 상위에 낯선 발생원이 와 있지 않은지, 장애가 발생한 시간대에 대응하는 건수의 산이 있는지를 보는 것이 첫 번째 단계입니다. 여기서 짐작을 잡은 후 다음 장의 레시피로 개별 이벤트를 파고듭니다.
해당 이벤트가 한 건도 없는 경우 Get-WinEvent는 오류를 냅니다. 여기서 안이하게 -ErrorAction SilentlyContinue를 붙이지 마십시오. 「해당 없음」도 「그 로그를 읽을 권한이 없음」도 「대상에 도달할 수 없음」도 모두 같은 “빈 결과”가 되어버립니다. 조사 상황에서는 이것이 가장 곤란한 망가짐 방식입니다.
위와 같이 -ErrorAction Stop으로 받아서 FullyQualifiedErrorId가 NoMatchingEventsFound일 때만 무시하는 것이 정답입니다. 메시지 문자열로 판정하면 한국어 환경에서는 일치하지 않아 동작하지 않습니다(오류 처리 방식은 「PowerShell의 오류 처리와 재시도 설계」).
4. 현장에서 자주 나오는 조사 레시피
(1) 예기치 않은 재시작・종료
# 41: 정상적인 종료 없이 재시작됨(Kernel-Power)
# 6008: 예기치 않은 종료(EventLog)
# 1074: 프로세스/사용자에 의한 종료 요청(누가・무엇이 내렸는가)
# 6005/6006: 이벤트 로그 서비스의 시작/중지(= 시작/종료의 표식)
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ID = 41, 1074, 6005, 6006, 6008
StartTime = (Get-Date).AddDays(-30)
} | Select-Object TimeCreated, Id, ProviderName,
@{ n = 'Message'; e = { ($_.Message -split "`r?`n")[0] } } |
Sort-Object TimeCreated -Descending | Format-Table -AutoSize
결과를 읽는 법. 출력은 TimeCreated / Id / ProviderName / Message(길기 때문에 첫 줄만)의 4열로, 최신순으로 정렬됩니다. 봐야 할 것은 한 줄씩의 내용보다 재시작 1회당 어떤 ID가 어떤 순서로 나열되는가입니다. 계획된 재시작이라면 1074(누군가 요청함)→ 6006(이벤트 로그 서비스 중지=정상 종료)→ 6005(시작=부팅 완료)가 한 세트로 나타납니다.
1074는 「누가 재시작을 요청했는가」를 알 수 있는 중요한 이벤트입니다. 여기에 WindowsUpdate나 특정 프로세스 이름이 나오면 원인이 거의 확정됩니다. 41과 6008만 나열되고 1074가 없는 경우, 전원 차단이나 행(hang)에 의한 비정상 정지를 의심합니다.
(2) 로그온・로그오프 추적(보안 로그)
# 4624: 로그온 성공 / 4625: 로그온 실패 / 4634: 로그오프
# 보안 로그 읽기 권한이 필요(관리자 권한으로 실행하거나,
# 실행 계정을 Event Log Readers 그룹에 추가해 둔다)
Get-WinEvent -FilterHashtable @{
LogName = 'Security'
ID = 4624, 4625, 4634
StartTime = (Get-Date).AddDays(-1)
} | ForEach-Object {
$xml = [xml]$_.ToXml()
$d = @{}
foreach ($n in $xml.Event.EventData.Data) { $d[$n.Name] = $n.'#text' }
[pscustomobject]@{
시각 = $_.TimeCreated
종류 = switch ($_.Id) { 4624 { '로그온 성공' } 4625 { '로그온 실패' } 4634 { '로그오프' } }
사용자 = $d['TargetUserName']
로그온종류 = $d['LogonType'] # 2=대화형 3=네트워크 10=RDP
접속원 = $d['IpAddress']
}
} | Where-Object 사용자 -notlike '*$' | Format-Table -AutoSize
여기가 이벤트 데이터를 사용하는 전형적인 예입니다. Message를 정규식으로 자르면 OS의 언어 설정에 의존하지만, ToXml()의 EventData는 이름으로 참조할 수 있으므로 한국어 환경에서도 영어 환경에서도 같은 스크립트가 동작합니다.6
결과를 읽는 법. 출력은 시각 / 종류 / 사용자 / 로그온종류 / 접속원의 5열입니다([pscustomobject]로 직접 조립하고 있으므로 열 이름도 순서도 직접 정할 수 있습니다). 끝부분에 Where-Object 사용자 -notlike '*$'를 붙여 컴퓨터 계정(이름이 $로 끝남)을 제외했으므로, 남는 것은 사람 계정입니다. 로그온종류가 3(네트워크)이나 10(RDP)인 행에서 접속원에 낯선 IP 주소가 없는지, 같은 사용자의 4625(실패)가 짧은 시간에 연속되지 않는지를 봅니다.
감사가 활성화되어 있지 않으면 애초에 이벤트가 기록되지 않는다는 점에도 주의하십시오. 「한 건도 나오지 않는다」는 「아무 일도 일어나지 않았다」가 아니라 「기록되지 않았다」일 수 있습니다.
조사 담당자에게 「읽기만」 부여한다. 보안 로그를 읽게 하기 위해 관리자 권한을 나눠 주는 것은 피하고 싶은 부분입니다. 내장 Event Log Readers 그룹(SID는 S-1-5-32-573)에 계정을 추가하면 관리자 권한 없이 이벤트 로그를 읽을 수 있게 됩니다.2
# 대상 컴퓨터에서 실행한다(관리자 권한 필요).
# 내장 그룹의 표시 이름은 OS 언어에 따라 달라질 수 있으므로 SID로 지정하는 것이 확실
Add-LocalGroupMember -SID 'S-1-5-32-573' -Member 'EXAMPLE\감사담당'
# 추가되었는지 확인한다
Get-LocalGroupMember -SID 'S-1-5-32-573'
여러 대에 배포하려면 그룹 정책으로 「컴퓨터 구성 > 정책 > Windows 설정 > 보안 설정 > 제한된 그룹」, 또는 그룹 정책 기본 설정 「컴퓨터 구성 > 기본 설정 > 제어판 설정 > 로컬 사용자 및 그룹」을 사용해, 도메인 그룹을 각 서버의 로컬 Event Log Readers에 추가하는 설정을 배포합니다.
채널 단위로 더 좁히고 싶다면 로그별 액세스 허용(SDDL)을 wevtutil로 확인・변경합니다.2
wevtutil gl Security # 현재 설정을 표시한다(channelAccess가 SDDL)
# wevtutil sl Security /ca:<SDDL> # 변경한다. /ca는 기존 SDDL을 「대체」한다
/ca는 추가가 아니라 대체입니다. 반드시 wevtutil gl의 출력을 기록해 둔 후 변경하십시오.
(3) 애플리케이션의 비정상 종료
# 1000: Application Error(앱 크래시)
# 1026: .NET Runtime(관리되는 예외에 의한 종료)
# 1001: Windows Error Reporting(장애 버킷 정보)
Get-WinEvent -FilterHashtable @{
LogName = 'Application'
ID = 1000, 1001, 1026
StartTime = (Get-Date).AddDays(-14)
} | Select-Object TimeCreated, Id, ProviderName, Message |
Sort-Object TimeCreated -Descending | Format-List
1000에는 다운된 모듈 이름과 오프셋이, 1026에는 .NET 예외 스택이 들어 있습니다. 여기서 짐작을 잡은 후 덤프 분석으로 넘어가는 것이 효율적입니다(「Windows 크래시 덤프 수집 입문」「WinDbg + SOS로 크래시 덤프 읽기」).
(4) 서비스 중지・재시작
# 7034: 서비스가 예기치 않게 종료됨 / 7031: 종료 후 복구 동작 / 7045: 새 서비스가 설치됨
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'Service Control Manager'
ID = 7031, 7034, 7045
StartTime = (Get-Date).AddDays(-30)
} | Select-Object TimeCreated, Id, Message | Format-List
7045(새 서비스 설치)는 의도하지 않은 소프트웨어 설치를 감지하는 용도로도 유용합니다. Windows 서비스의 운영 설계는 「Windows 서비스 만드는 방법과 운영」을 참조하십시오.
(5) 작업 스케줄러 실행 이력
이 절에서는 XPath를 사용하므로, 먼저 골격을 짚어 두겠습니다. 이벤트 로그의 XPath에서 외워야 할 것은 실질적으로 2종류뿐입니다.1
| 작성 방식 | 가리키는 것 | 예 |
|---|---|---|
*[System[ ... ]] |
어떤 이벤트에나 공통되는 항목(이벤트 ID, 시각, 수준, 제공자) | *[System[EventID=41]] |
*[EventData[Data[@Name='항목명']='값']] |
그 이벤트 고유의 항목(이름이 붙은 이벤트 데이터) | *[EventData[Data[@Name='TaskName']='\야간집계']] |
이 둘을 and / or로 연결하기만 하면 됩니다. 값 비교에는 작은따옴표를 사용하므로, PowerShell 쪽에서는 히어스트링(@" ~ "@)에 넣으면 인용부호의 중첩으로 고민할 필요가 없습니다(아래 코드가 그 형태입니다).
직접 조립하기보다 이벤트 뷰어에게 작성시켜 복사하는 편이 빠르고 확실합니다. 「이벤트 뷰어 > (대상 로그를 마우스 오른쪽 클릭) > 현재 로그 필터링」에서 GUI로 조건을 지정하고, 대화 상자의 「XML」 탭으로 전환하면 같은 조건의 쿼리가 XML로 표시됩니다. 그 <Select Path="...">와 </Select> 사이에 있는 부분이 그대로 -FilterXPath에 전달할 수 있는 문자열입니다.1
# 【NG】전체를 가져온 후, 표시용 메시지(언어 의존)로 좁힌다
Get-WinEvent -FilterHashtable @{
LogName = 'Microsoft-Windows-TaskScheduler/Operational'
StartTime = (Get-Date).AddDays(-3)
} -ErrorAction SilentlyContinue |
Where-Object { $_.Message -like '*야간집계*' }
# 【OK】작업 이름(이벤트 데이터)으로 서버 쪽에서 좁힌다. 빠르고 언어에도 의존하지 않는다
$xpath = @"
*[System[TimeCreated[timediff(@SystemTime) <= 259200000]]]
and
*[EventData[Data[@Name='TaskName']='\야간집계']]
"@
Get-WinEvent -LogName 'Microsoft-Windows-TaskScheduler/Operational' `
-FilterXPath $xpath -ErrorAction SilentlyContinue |
Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List
timediff는 밀리초 단위로 「현재로부터의 경과 시간」을 지정하는 XPath 함수로, 259200000은 3일입니다. 작업 이름은 등록 시의 전체 경로(루트 바로 아래라면 \작업이름)로 지정합니다. 표시용 Message는 언어 설정에 의존하고, 게다가 클라이언트 쪽에서의 필터링이 되어버리므로, 이 글의 방침대로 이벤트 데이터로 좁혀 주십시오.
이 로그는 기본적으로 비활성화되어 있는 경우가 있습니다. 활성화 방법과 작업이 동작하지 않을 때의 원인 분리는 「작업 스케줄러의 작업이 실행되지 않는다・0x1로 끝난다」에 정리되어 있습니다.
5. 여러 대・다른 머신의 로그를 조사한다
방법 1: 원격으로 직접 읽는다
Get-WinEvent -ComputerName 'srv01' -FilterHashtable @{ LogName='System'; ID=41 } -MaxEvents 10
방법 2: Invoke-Command로 병렬로 조회한다(대수가 많을 때는 이쪽)
$servers = 'srv01', 'srv02', 'srv03'
Invoke-Command -ComputerName $servers -ScriptBlock {
Get-WinEvent -FilterHashtable @{
LogName = 'System'; Level = 1, 2; StartTime = (Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue |
Select-Object TimeCreated, Id, ProviderName, LevelDisplayName
} | Sort-Object PSComputerName, TimeCreated
Invoke-Command는 여러 컴퓨터에 대해 병렬로 실행됩니다(「PowerShell Remoting(WinRM) 입문」「PowerShell의 병렬 처리」).
방법 3: .evtx를 채취해 로컬에서 분석한다
현지에서 내보낸 파일을 그대로 읽을 수 있습니다. 네트워크 너머로 몇 번씩 조회하는 것보다 빠르고, 증거로서도 남습니다.
# 현지에서: wevtutil epl System D:\collect\srv01_System.evtx
Get-WinEvent -Path 'D:\collect\srv01_System.evtx' -FilterXPath "*[System[(Level=1 or Level=2)]]" |
Select-Object TimeCreated, Id, ProviderName, Message
방법 4: Windows 이벤트 전달(WEF) ── 상시로 집약한다면 이것입니다. 수집 서버에 각 단말의 이벤트를 전달하는 표준 기능으로, 에이전트를 추가로 설치할 필요가 없습니다.5
6. 로그의 크기와 보존 기간
「조사하려고 했더니 그 시간대의 로그가 이미 덮어써져 있었다」는 매우 흔한 실패입니다. 기본 최대 크기는 작은 편이라, 이벤트가 많은 환경에서는 며칠 만에 한 바퀴 돕니다.
# 현재 크기 설정과 보존 상태를 확인한다
Get-WinEvent -ListLog 'System', 'Application', 'Security' |
Select-Object LogName, IsEnabled, LogMode,
@{ n='MaxMB'; e={ [math]::Round($_.MaximumSizeInBytes / 1MB, 1) } },
RecordCount, OldestRecordNumber
# 최대 크기를 변경한다(관리자 권한. 예: System 로그를 256MB로)
wevtutil sl System /ms:268435456
조사를 전제로 하는 서버나 장애가 발생하고 있는 단말에서는 먼저 로그 크기를 넓혀 두고 재현을 기다리는 것이 정석입니다. 로그의 세대 관리와 자동 아카이브에 대해서는 「PowerShell 스크립트 응용 ── 로그 조사・아카이브・리포트화」도 참조하십시오.
7. 실무의 정석(판단표)
| 상황 | 선택 | 보충 |
|---|---|---|
| 앞으로 작성하는 스크립트 | Get-WinEvent |
Get-EventLog는 5.1 전용・클래식 로그만1 |
| 로그 이름・ID・기간으로 좁힘 | -FilterHashtable |
가장 빠르고 읽기 쉬움3 |
| 이벤트 데이터 내용으로 좁힘 | -FilterXPath / -FilterXml |
이벤트 뷰어의 사용자 지정 보기에서 복사할 수 있음1 |
| 건수가 많아 시간이 걸림 | 기간을 좁힌다・-MaxEvents |
필터링을 PowerShell 쪽에서 하지 않는다 |
| 메시지에서 값을 뽑고 싶음 | ToXml()의 EventData |
언어 설정에 의존하지 않는 스크립트가 됨1 |
| 여러 대를 단발성 조사 | Invoke-Command |
병렬로 실행됨5 |
| 상시로 집약 | Windows 이벤트 전달(WEF) | 추가 에이전트 불필요한 표준 기능5 |
| 과거 로그가 사라져 있음 | 로그 크기 확장 | 재현을 기다리기 전에 해 둘 것 |
8. 정리
Get-WinEvent로 통일합니다.Get-EventLog는 PowerShell 7에서 사용할 수 없고, 다룰 수 있는 로그도 한정되어 있습니다.- 필터링은
-FilterHashtable이나-FilterXPath로 수행합니다.Where-Object에 의한 후단 필터링은 조사 시간을 자릿수 단위로 악화시킵니다. - 재시작 조사는 41・6008에 더해 1074(누가 요청했는가)를 봅니다. 앱의 비정상 종료는 1000・1026・1001이 출발점입니다.
- 메시지 문자열이 아니라
ToXml()의 이벤트 데이터를 사용하면 언어 설정에 의존하지 않는 스크립트가 됩니다. - 여러 대의 단발성 조사는
Invoke-Command, 상시 집약은 Windows 이벤트 전달, 증거가 필요하다면.evtx채취와 로컬 분석입니다. - 조사하고 싶은 시간대의 로그가 남아 있지 않으면 아무것도 할 수 없습니다. 로그 크기 재검토는 장애 대응 준비로서 최우선입니다.
샘플 코드 다운로드
이 글에서 다룬 코드는 그대로 실행할 수 있는 형태로 정리해 배포하고 있습니다. 재시작 이력・로그온 이력・작업 실패・여러 대 수집이 포함되어 있습니다.
이 글의 샘플은 Windows나 테넌트에 의존하기 때문에 실행 검증은 하지 않았습니다. 구문 분석과 PSScriptAnalyzer에 의한 정적 분석까지는 모든 파일에 대해 실시했지만, 동작은 반드시 직접 검증 환경에서 확인해 주십시오.
# 구문 분석 + 정적 분석(Windows가 아니어도 실행 가능)
./Invoke-SampleTests.ps1
시도해 볼 곳이 없다는 경우, 업무 단말에서 실제 운영 로그를 상대로 연습할 필요는 없습니다. Windows 샌드박스라면 몇 분 만에 깨끗한 Windows를 시작할 수 있고, 닫으면 사라집니다(「Windows 샌드박스로 앱 검증을 빠르게 하는 방법」). 다만 샌드박스는 시작 직후의 환경이라 로그가 거의 쌓여 있지 않으므로, 재시작 이력이나 로그온 이력처럼 「시간이 지나면서 쌓이는 로그」를 시험하려면 평가용 가상 머신 쪽이 적합합니다. 먼저 3장의 명령어를 직접 단말에서 실행해, 필터링 속도의 차이를 체감하는 것부터로 충분합니다.
설정값(경로, 서버 이름, 테넌트 ID 등)은 예시입니다. 그대로 운영 환경에서 실행하지 말고, 자사 환경에 맞게 바꿔서 사용해 주십시오.
관련 글
- 작업 스케줄러 작업이 실행되지 않는다・0x1로 끝난다 ── 원인 분리와 안전한 운영 설계
- Windows 이벤트 로그·ETW 입문 ── 업무 앱의 로그를 OS 표준 체계에 올리기
- Windows 앱의 크래시 덤프 수집 입문 - 우선 WER / ProcDump / WinDbg를 어떻게 구분해서 쓸까
- PowerShell Remoting(WinRM) 입문 ── 여러 대의 Windows를 일괄 관리하기
- Windows 서비스 만드는 방법과 운영 ── 태스크 스케줄러와의 구분 사용부터 BackgroundService의 서비스화까지
- PowerShell 스크립트 응용 ── 로그 조사·아카이브·리포트화를 안전하게 자동화하기
관련 상담 영역
합동회사 코무라소프트에서는 Windows 환경의 장애 조사, 간헐적으로 발생하는 재시작・앱 비정상 종료의 원인 분석, 로그 수집과 모니터링 체계 구축을 다루고 있습니다.
참고 링크
-
Microsoft Learn, Get-WinEvent. 클래식 로그와 Windows Vista 이후의 이벤트 로그를 모두 가져올 수 있다는 점, -ListLog / -ListProvider에 의한 목록 가져오기, -FilterHashtable / -FilterXPath / -FilterXml에 의한 필터링, -Path에 의한 보관된 로그(.evtx)의 읽기, -ComputerName에 의한 원격 가져오기, -MaxEvents에 의한 건수 제한, 보안 로그를 읽으려면 관리자 권한이 필요하다는 점, 각 이벤트의 ToXml() 메서드로 XML 표현을 얻을 수 있다는 점에 대해. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11 ↩12 ↩13 ↩14 ↩15
-
Microsoft Learn, Active Directory security groups ─ Event Log Readers. 내장 「Event Log Readers」 그룹의 구성원이 로컬 컴퓨터의 이벤트 로그를 읽을 수 있다는 점(관리자 권한 부여를 수반하지 않는다는 점)에 대해. 개별 채널의 액세스 허용을 변경하는 방법으로는 wevtutil의 sl /ca(채널 액세스)도 참조. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Creating Get-WinEvent queries with FilterHashtable. -FilterHashtable로 지정할 수 있는 키(LogName・ProviderName・Path・Keywords・ID・Level・StartTime・EndTime・UserID・Data 등), 서버 쪽에서 필터링되므로 Where-Object에 의한 필터링보다 효율적이라는 점, 키워드나 심각도 값의 지정 방법에 대해. ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Event Levels. 이벤트 심각도 수준의 표준값(1=Critical, 2=Error, 3=Warning, 4=Informational, 5=Verbose)에 대해. ↩ ↩2
-
Microsoft Learn, Windows Event Forwarding. 에이전트를 추가로 설치하지 않고도 여러 Windows에서 수집 서버로 이벤트를 전달할 수 있다는 점, 구독을 통한 수집 대상 지정에 대해. 아울러 Invoke-Command에 의한 여러 컴퓨터로의 병렬 실행에 대해. ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, 4624(S): An account was successfully logged on. 로그온 성공 이벤트의 이벤트 데이터에 포함된 항목(TargetUserName, LogonType, IpAddress 등)과 로그온 종류 값의 의미, 감사 정책 설정에 따라 기록 여부가 달라진다는 점에 대해. ↩
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
PowerShell의 보안 강화 ── 로그·AMSI·언어 모드·JEA
PowerShell을 금지하지 않고 안전하게 사용하기 위한 실무를 정리합니다. 스크립트 블록 로그와 트랜스크립션 활성화, AMSI와 구버전 무효화, 언어 모드에 의한 제한, JEA에 의한 권한 위임까지 설명합니다.
Windows 보안 감사 정책과 이벤트 로그 조사 실무 ── 4625를 읽어내는 정보시스템 담당자 되기
「로그온 실패 로그를 조사해 달라」는 요청에 대응하기 위한 실무 가이드입니다. 기본 감사 정책과 고급 감사 정책의 관계, 최소한 활성화해야 할 하위 범주, 이벤트 ID 4624/4625/4688을 읽는 법, Security 로그의 용량 설계, G...
Windows 인증서 저장소 실무 가이드 ── 사용자와 컴퓨터, 어느 쪽에 넣어야 하는가
클라이언트 인증서는 사용자와 컴퓨터 중 어느 저장소에 넣어야 할까. certmgr.msc와 certlm.msc의 차이, 비밀 키 권한 부여, PowerShell을 이용한 만료 점검까지, 인증서의 단골 사고를 체계적으로 없애는 실무 가이드입니다.
SMB 서명과 LDAP 채널 바인딩 ── NTLM 대책의 '나머지 절반'을 실무에서 마무리하기
NTLM을 중단하기까지의 기간 동안 릴레이 공격의 피해를 억제하는 방어책이 SMB 서명과 LDAP 서명·채널 바인딩입니다. OS별 기본값, 감사 이벤트를 읽는 법, 강제 적용으로 나아가는 절차, 업무 앱과 기기를 고치는 방법까지 실무 관점에서 정...
NTLM 폐지로 업무 앱이 멈추는가 ── 감사 로그를 얻는 방법과 의존을 없애는 순서
NTLM 폐지에 대비해 자사 Windows 환경과 업무 앱이 어디에서 NTLM에 의존하고 있는지 찾아내는 절차를 정리합니다. 감사 정책, NTLM/Operational 로그의 이벤트 8001~8004 추적, NTLM으로 떨어지는 전형적인 패턴과 ...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
장애 조사 & 장기 가동 장애
간헐적 장애, 통신 진단, 장기 가동 크래시, 실패 경로 테스트 기반을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
장애 조사 & 원인 분석
간헐적 장애, 장기 가동 중 크래시, 누수, 통신 중단 등 까다로운 프로덕션 이슈를 조사합니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- Get-EventLog와 Get-WinEvent 중 어느 쪽을 사용해야 하나요?
- Get-WinEvent입니다. Get-EventLog는 Windows PowerShell 5.1에서만 사용할 수 있고, System·Application·Security와 같은 전통적인(클래식) 로그만 다룰 수 있습니다. Windows Vista 이후에 추가된 「애플리케이션 및 서비스 로그」 아래의 로그, 예를 들어 Microsoft-Windows-TaskScheduler/Operational 같은 상세한 로그는 Get-WinEvent가 아니면 읽을 수 없습니다. PowerShell 7에서는 Get-EventLog 자체를 사용할 수 없으므로, 앞으로 작성하는 스크립트는 Get-WinEvent로 통일하십시오.
- Get-WinEvent가 느려서 조사가 되지 않습니다.
- 파이프 뒤쪽에서 Where-Object로 필터링하고 있을 가능성이 높습니다. 그런 작성 방식에서는 먼저 로그의 모든 이벤트를 객체화해 PowerShell로 읽어들인 다음, 그 후에 불필요한 것을 버리게 됩니다. 수십만 건의 로그에서는 비현실적입니다. -FilterHashtable이나 -FilterXPath를 사용하면 필터링이 이벤트 로그 쪽에서 이루어져 필요한 이벤트만 반환되므로 차원이 다르게 빨라집니다. 먼저 로그 이름・기간・이벤트 ID를 FilterHashtable로 지정하는 습관을 들이십시오.
- 보안 로그를 읽으려고 하면 액세스가 거부됩니다.
- 보안 로그를 읽으려면 기본적으로 특별한 권한이 필요합니다. 직접 확인만 하는 것이라면 PowerShell을 「관리자 권한으로 실행」하면 되지만, 조사 담당자에게 관리자 권한을 넘기고 싶지 않다면 내장 「Event Log Readers(이벤트 로그 읽기 권한자)」 그룹에 추가하거나, 채널의 액세스 허용(ACL)으로 읽기만 허용하는 방법이 있습니다. 최소 권한의 관점에서는 후자를 권장합니다. 또한 애초에 감사 로그가 기록되어 있지 않은 경우도 있습니다. 로그온 감사 등은 감사 정책 설정에 따라 달라지므로, 이벤트가 한 건도 발견되지 않을 때는 정책 자체가 활성화되어 있는지도 확인하십시오.
- 이벤트 메시지에서 특정 값(사용자 이름이나 프로세스 이름)만 추출하고 싶습니다.
- Message 속성을 정규식으로 잘라내는 것보다 Properties나 이벤트 데이터를 사용하는 편이 확실합니다. 각 이벤트는 ToXml() 메서드로 XML 표현을 얻을 수 있으며, 거기에는 EventData 아래에 이름이 붙은 항목이 들어 있습니다. 표시용 메시지 문자열은 OS의 언어 설정에 따라 달라지지만, 이벤트 데이터의 구조는 변하지 않으므로 한국어 환경과 영어 환경 양쪽에서 동작하는 스크립트를 작성할 수 있습니다.
- 여러 대의 서버 이벤트 로그를 한꺼번에 조사하고 싶습니다.
- 대수가 적다면 Get-WinEvent의 -ComputerName 매개변수나, Invoke-Command를 이용한 원격 실행으로 충분합니다. Invoke-Command는 지정한 여러 대에 대해 병렬로 실행되므로 수십 대 정도라면 실용적입니다. 상시로 집약하고 싶다면 Windows 이벤트 전달(WEF)로 수집 서버에 모으는 구조를 검토해 주십시오. 단발성 조사라면 각 서버에서 evtx 파일을 내보내고, 로컬에서 Get-WinEvent -Path로 분석하는 방법도 유효합니다.