WinRM
개요
WinRM은 Windows Remote Management의 약자로 표준 SOAP(Simple Object Access Protocol) 기반 방화벽 친화적 프로토콜인 WS-Management 프로토콜 기반으로 추가적인 Agent 설치 없이 Windows 서버를 원격에서 접근하고 제어할 수 있는 Microsoft 구현체 입니다.
스펙
- 운영체제
- Windows Server 2008 (with SP2) - only supported version 3.0 이상
- Windows Server 2008 R2 (with SP1) 이상
- Windows 7 (with SP1) 이상
- PowerShell
- 5.1 이상
- .NET Framework
- 4.0 이상
- 네트워크
- 5985/http
- 5986/https
WinRM 구성
관리자 서버
#
# 1. 사전 확인
# Powershell 버전 확인 : 3.0+
#
$PSVersionTable
#
# 2. PowerShell 원격 활성화 설정
# 네트워크 카테고리가 Public이면 WinRM Authentication Basic을 사용할 수 없다.
#
Enable-PSRemoting -SkipNetworkProfileCheck -Force
# Authentication Basic일 때
# Set-NetConnectionProfile -NetworkCategory Private
# Get-NetConnectionProfile
#
# - 요청을 수신하도록 WinRM이 업데이트되었습니다.
# - WinRM 서비스 종류가 변경되었습니다.
# - WinRM 서비스를 시작했습니다.
# - 원격 관리를 위한 WinRM이 업데이트되었습니다.
# - WinRM 방화벽 예외를 사용합니다.
# - 로컬 사용자에게 원격으로 관리자 권한을 부여하도록 LocalAccountTokenFilterPolicy를 구성했습니다.
#
# Disable-PSRemoting -Force
#
# winrm get winrm/config/service
# Get-ChildItem WSMan:\localhost\service
#
# winrm e winrm/config/listener
# Get-ChildItem WSMan:\localhost\Listener
#
# 3. 5985/tcp 방화벽 허용
#
New-NetFirewallRule -DisplayName "WinRM-HTTP-In-TCP" -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow
# Set-NetFirewallProfile -Profile Domain, Public, Private -Enabled True
# Get-NetFirewallRule -Name 'WINRM-HTTP-In-TCP' | Select-Object -Property Name, Direction, Action
# Get-NetFirewallRule -Name winrm* | Select-Object Name, DisplayName, Profile, Enabled | Format-Table
#
# 4. TrustedHosts 신대 대상 추가
#
Set-Item WSMAN:\localhost\Client\TrustedHosts * -Force | Out-Null
# Get-Item WSMAN:\localhost\Client\TrustedHosts
#
# 5. (Ansible 접속일 때 필수)RootSDDL에 계정 추가
# - Add-WinRMDaclRule.ps1: https://gist.github.com/jborean93/6d9aaf868d1d40344188984ebb431b04
# - AddSecurityPrincipalonDefaultWinRMSDDL.ps1: https://gist.github.com/jfrmilner/84314d79bf2708abff15313ff5f5d1ee
#
winrm configSDDL default
#
# 6. PowerShell 추가
#
Set-PssessionConfiguration microsoft.powershell -showsecuritydescriptor
# Get-PSSessionConfiguration | Format-Table -Property Name, Permission
# Name Permission
# ---- ----------
# microsoft.powershell NT AUTHORITY\INTERACTIVE AccessAllowed,
# BUILTIN\Administrators AccessAllowed,
# BUILTIN\Remote Management Users AccessAllowed,
# {Non-Administrator 계정}
#
# 설정 확인
#
Get-Service -Name "WinRM"
Get-LocalGroupMember -Group "Remote Management Users"
ObjectClass Name PrincipalSource
----------- ---- ---------------
User {호스트 이름}\{계정 이름} Local
원격 서버
$userName = "{계정}"
$password = "{암호}"
# 사용자 생성
New-LocalUser -Name $userName -Password $securePassword -PasswordNeverExpires:$true -UserMayNotChangePassword:$true -ErrorAction Stop
# Administrators 그룹 추가
Add-LocalGroupMember -Group "Administrators" -Member $userName -ErrorAction Stop
# PSRemoting 활성화
Enable-PSRemoting -SkipNetworkProfileCheck -Force
# PSRemoting 5985/http 방화벽 허용
New-NetFirewallRule -DisplayName "WinRM-HTTP-In-TCP" -Direction Inbound -Protocol TCP -LocalPort 5985 -Action Allow | Out-Null
#
# 설정 확인
#
Get-LocalUser -Name $userName
Get-LocalGroupMember -Group "Administrators" -Member $userName
Get-Service -Name "WinRM"
Get-NetFirewallRule -Name 'WINRM-HTTP-In-TCP' | Select-Object -Property Name, Direction, Action
Test-NetConnection -Port 5985 -ComputerName localhost -WarningAction SilentlyContinue
WinRM 접속
접속
$servers = "{IP}", "{IP}"
$username = "{계정}" # whoami
$password = "{암호}"
$securepw = $password | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $securepw
$sessions = New-PSSession -ComputerName $servers -Credential $creds -Authentication negotiate -SessionOption (New-PSSessionOption -SkipCACheck -SkipCNCheck)
$sessions
New-PSSession
: non 대화형(명령형)Enter-PSSession
: 대화형
접속 해지
Remove-PSSession $sessions
WinRM 명령 구성
입력
Invoke-Command -Session $sessions -ScriptBlock {
param($remote변수1, $remote변수2, $remoteServerIp)
...
} -ArgumentList $변수1, $변수2
출력
$result = Invoke-Command -Session $sessions -ScriptBlock {
return [PSCustomObject]@{
Timestamp = Get-Date
HostName = $env:COMPUTERNAME
필드_이름1 = $변수1
}
}
$result.Timestamp
$result.HostName
$result.필드_이름1
함수
Invoke-Command -Session $sessions -ScriptBlock {
# 함수 정의
function Get-ServiceProcessPID {
param(
[string]$serviceName
)
# ...
# 사용자 정의 출력 타입
return Get-WmiObject -Class Win32_Service |
Where-Object { $_.Name -eq $serviceName } |
Select-Object -ExpandProperty ProcessId
}
# 함수 호출
$serviceProcessPID = Get-ServiceProcessPID -serviceName $serviceName
}
WinRM 주요 명령
원격 서버 hostname
Invoke-Command -Session $sessions -ScriptBlock {
hostname
}
원격 서버 정보
Invoke-Command -Session $sessions -ScriptBlock {
return [PSCustomObject]@{
HostName = hostname
Now = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
PSVersion = $PSVersionTable.PSVersion
}
} |
Format-Table -Property @{Expression={$_.HostName}; Label="Host Name"},
@{Expression={$_.PSComputerName}; Label="Host IP"},
@{Expression={$_.Now}; Label="Host Now"},
@{Expression={$_.PSVersion}; Label="PSVersion"}
원격 서버 윈도우 서비스 중지
Invoke-Command -Session $sessions -ScriptBlock {
$service = Get-Service "서비스_이름" -ErrorAction SilentlyContinue
if ($service) {
Stop-Service $serviceName -Force -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
}
}
원격 서버 윈도우 서비스 Timeout 중지
function Stop-ServiceAndWait {
param(
[string]$serviceName, # 서비스 이름
[int]$timeoutSeconds # 시간(초)
)
$scriptBlock = { param($serviceName) Stop-Service -Name $serviceName -Force -PassThru -WarningAction SilentlyContinue -ErrorAction SilentlyContinue }
$stopJob = Start-Job -ScriptBlock $scriptBlock -ArgumentList $serviceName
$completed = $stopJob |
Wait-Job -Timeout $timeoutSeconds -WarningAction SilentlyContinue -ErrorAction SilentlyContinue
if ($completed) {
$jobResult = Receive-Job -Job $stopJob
if ($jobResult.Status -eq "Stopped") {
#Write-Host "서비스가 성공적으로 중지되었습니다."
return $True
} else {
#Write-Host "서비스 중지에 실패했습니다."
return $False
}
}
else {
#Write-Host "서비스 중지 작업이 시간 내에 완료되지 않았습니다."
$stopJob | Remove-Job -Force
return $False
}
}
원격 서버 프로세스 강제 종료
Invoke-Command -Session $sessions -ScriptBlock {
Start-Process -FilePath "taskkill.exe" -ArgumentList "/F /IM 프로세스이름.exe" -NoNewWindow -Wait
}