使用 PowerShell 將 Logstash 設為 Windows 服務
25 November 2017 on logstash powershell. 10 minutes
由於近來常常在 Windows Server 2012 R2 上手動安裝 Logstash 5.x,因為實在太繁瑣耗時,而官方又沒有 提供自動化的安裝程式,所以就用 PowerShell 寫了這支 script,也順便趁這個機會學習 PowerShell。
以下有幾點注意事項:
- 僅支援 PowerShell 5.1 以上版本。
- 本程式將以 Administrator 身份執行。
- 請先安裝 Java 8 (或更高版本)。
最後,希望這支 Script 對有類似需要的朋友們有所幫助。
# Logstash 自動安裝程式
#
# 參考:
#
# 1. [PowerShell](https://docs.microsoft.com/zh-tw/powershell/scripting/powershell-scripting?view=powershell-5.1)
#
# @since 2017-11-25 09:00:12
# @author Gary Liu<desp0916@gmail.com>
#
# ===================== 函式 =====================
# 檢查是否有安裝 Java?
function CheckJavaInstalled {
Try {
Start-Process java -ArgumentList "-version" -NoNewWindow -Wait -ErrorAction SilentlyContinue
return $?
} Catch {
return $false
Break
}
}
# 取得目前系統中的 Java 版本
function GetJavaVersion {
$p = Start-Process java -ArgumentList "-version" -NoNewWindow -RedirectStandardError .\javaver.txt -Wait
$JavaVersion = ((Get-Content .\javaver.txt) -split '\n')[0]
$x = del .\javaver.txt
return $JavaVersion
}
# 判斷 Java 版本是否大於 8?
function CheckJava8OrAbove {
param ([string] $javaVersion)
$distribution,$string,$verStr=$javaVersion.Split(' ')
$major,$minor,$rev=($verStr -replace '"', '').Split('.')
$equalOrGreaterThanEight = $minor -ge 8
return $equalOrGreaterThanEight
}
# 取得 script 目前所在的路徑
# https://stackoverflow.com/a/19542600
function GetScriptDirectory {
$Invocation = (Get-Variable MyInvocation -Scope 1).Value;
if ($Invocation.PSScriptRoot) {
$Invocation.PSScriptRoot;
} Elseif ($Invocation.MyCommand.Path) {
Split-Path $Invocation.MyCommand.Path
} else {
$Invocation.InvocationName.Substring(0,$Invocation.InvocationName.LastIndexOf("\"));
}
}
# 解壓縮 Unzip
# https://stackoverflow.com/questions/27768303/how-to-unzip-a-file-in-powershell
function Unzip {
param([string]$zipfile, [string]$outpath)
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath)
}
# 取得 Logstash 的安裝路徑
function GetLogstashHomePath($LogstashZipFilePath) {
$ext = [IO.Path]::GetExtension($LogstashZipFilePath)
$LogstashHomePath = $LogstashZipFilePath.Replace($ext, "")
return $LogstashHomePath
}
# 建立本地使用者 aplogger
function CreateLocalUser($UserName, $Password) {
#$UserName = if(($result = Read-Host "請輸入 aplogger 的帳號名稱 [aplogger]") -eq ''){"aplogger"}else{$result}
#$UserName = Read-Host -Prompt "請輸入 aplogger 的帳號名稱"
#$Password = Read-Host -AsSecureString -Prompt "請輸入 aplogger 的密碼:"
#New-LocalUser -Name $UserName -Password $Password -FullName "AP Logger" -Description "AP Log 分析系統 agent"
Try {
$SecureStringPassword = ConvertTo-SecureString $Password -AsPlainText -Force
New-LocalUser -Name $UserName -Password $SecureStringPassword -FullName "AP Logger" -Description "AP Logger" -ErrorAction SilentlyContinue
return $true
} Catch [Exception] {Microsoft.PowerShell.LocalAccounts
Write-Host $_.Exception.Message -ForegroundColor Red
return $false
#Break
}
}
# 將 aplogger 加入 Remote Desktop Users 群組
function AddToRDPGroup($UserName, $RDPGroup = "Remote Desktop Users") {
Add-LocalGroupMember -Group $RDPGroup -Member $UserName
}
# 啟用 Windows Server 2012 R2 上的 Telnet Client
# https://stackoverflow.com/questions/7330187/how-to-find-the-windows-version-from-the-powershell-command-line
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx
function EnableTelnetClientOnWinServer {
# Windows Server 2012 R2 的 ProductType == VER_NT_SERVER (3)
$ProductType = (Get-CimInstance Win32_OperatingSystem).ProductType
# Windows Server 2012 R2 的 Version == 6.3
$Version = [System.Environment]::OSVersion.Version
If ($ProductType -eq 3 -and $Version.Major -eq 6 -and $Version.Minor -eq 3) {
Write-Host "啟用 Windows Server 2012 R2 上的 Telnet Client...."
Import-Module servermanager
Add-WindowsFeature telnet-client
return $True
}
}
# 將 logstash 的 bin 目錄設定到 PATH 環境變數之中
# https://docs.microsoft.com/zh-tw/powershell/module/microsoft.powershell.localaccounts/get-localuser?view=powershell-5.1
function AddLogstashBinToEnvPath($LogstashHomePath) {
Write-Host "==> 檢查 $LogstashHomePath 是否已存在於 PATH 環境變數中?" -ForegroundColor Green
$exits = $env:Path -split ";" | Foreach {$_.trim('\')} | Select-String -Pattern $([regex]::Escape($LogstashHomePath.trim('\')))
If ($exits) {
Write-Warning "$LogstashHomePath 已存在於 PATH 環境變數中。"
} Else {
Write-Host "將 $LogstashHomePath 加入系統的 PATH 環境變數中..."
# https://stackoverflow.com/questions/714877/setting-windows-powershell-path-variable
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";" + $LogstashHomePath + "\bin", [EnvironmentVariableTarget]::Machine)
}
}
# https://stackoverflow.com/questions/2602460/powershell-to-manipulate-host-file
# https://stackoverflow.com/questions/13279580/how-to-interpolate-variables-in-regular-expressions-in-powershell
# Uncomment lines with $HostName on them:
function UncommentSomeHost($HostName) {
$hostsPath = "$env:windir\System32\drivers\etc\hosts"
$hosts = Get-Content $hostsPath
$hosts = $hosts | Foreach {if ($_ -match "^\s*#\s*(.*?\d{1,3}.*?$([regex]::Escape($HostName)).*)")
{$matches[1]} else {$_}}
$hosts | Out-File $hostsPath -enc ascii
}
# Comment lines with $HostName on them:
function CommentSomeHost($HostName) {
$hostsPath = "$env:windir\System32\drivers\etc\hosts"
$hosts = Get-Content $hostsPath
$hosts | Foreach {if ($_ -match "^\s*([^#].*?\d{1,3}.*?$([regex]::Escape($HostName)).*)")
{"# " + $matches[1]} else {$_}} |
Out-File $hostsPath -enc ascii
}
# 取得本地使用者帳號
function GetLocalUser($UserName) {
# Powershell 5.1:
$User = Get-LocalUser -Name $UserName -ErrorAction SilentlyContinue
# $User = Get-WmiObject -Class Win32_UserAccount -Filter "LocalAccount='True' and Name='$UserName'" -ErrorAction SilentlyContinue
return $User
}
# 修改 AP Log 目錄的存取權限
# https://technet.microsoft.com/zh-tw/library/ff730951.aspx
# https://msdn.microsoft.com/zh-tw/library/system.security.accesscontrol.filesystemrights(v=vs.110).aspx
function ChangeApLogPathACL($Username, $ApLogPath) {
$colRights = [System.Security.AccessControl.FileSystemRights]"ReadAndExecute, ListDirectory"
$InheritanceFlag = [System.Security.AccessControl.InheritanceFlags]::ObjectInherit
$PropagationFlag = [System.Security.AccessControl.PropagationFlags]::InheritOnly
$objType =[System.Security.AccessControl.AccessControlType]::Allow
$objUser = New-Object System.Security.Principal.NTAccount($Username)
$objACE = New-Object Security.AccessControl.FileSystemAccessRule "$env:COMPUTERNAME\$UserName", 'ReadAndExecute, ListDirectory', 'ContainerInherit, ObjectInherit', 'InheritOnly', 'Allow'
$objACL = Get-ACL $ApLogPath
$objACL.AddAccessRule($objACE)
Set-ACL $ApLogPath $objACL
}
# ===================== 主程式開始 =====================
# 重要設定
$UserName = "aplogger"
$Password = "Pa$$w0rd"
#$WorkingPath = GetScriptDirectory
$WorkingPath = $PSScriptRoot
$ApLogPath = "C:\log"
$SystemHostsPath = "$env:windir\System32\drivers\etc\hosts"
$APLogHostFile = "hosts-staging.txt"
$APLogHostFilePath = "$($WorkingPath)\$($APLogHostFile)"
$LogstashZipFile = "logstash-5.6.4.zip"
$LogstashZipFilePath = "$($WorkingPath)\$($LogstashZipFile)"
$LogstashHomePath = GetLogstashHomePath $LogstashZipFilePath
$ServiceName = "Logstash"
$RDPGroup = "Remote Desktop Users"
$PSVersion = $PSVersionTable.PSVersion
# 檢查 Powershell 版本
Write-Host "==> 檢查 Powershell 是否為 5.1 以上版本?" -ForegroundColor Green
$PSVersion | Format-Table
If (-Not ($PSVersion.Major -ge 5 -and $PSVersion.Minor -ge 1)) {
Write-Warning "本程式僅支援 Powershell 5.1 以上版本,本程式即將於 10 秒後結束。"
Write-Warning "建議升級至 Windows Management Framework 5.1 或以上版本:"
Write-Warning "https://docs.microsoft.com/zh-tw/powershell/wmf/5.1/install-configure"
Start-Sleep -s 10
exit
}
Add-Type -AssemblyName System.IO.Compression.FileSystem
Set-location $WorkingPath
# 本 Script 必須以 Administrator 的身份執行!
# https://stackoverflow.com/questions/7690994/powershell-running-a-command-as-administrator
Write-Host "==> 以 Administrator 身份執行...." -ForegroundColor Green
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$arguments = "& '" + $MyInvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
exit
}
EnableTelnetClientOnWinServer
Write-Host "==> 檢查本機帳號 $UserName 是否已存在?" -ForegroundColor Green
If (GetLocalUser($UserName)) {
Write-Warning "本機帳號 $UserName 已存在。"
} Else {
Write-Host "本機帳號 $UserName 不存在!建立本機帳號 $UserName..."
CreateLocalUser $UserName $Password
}
Write-Host "==> 檢查本機帳號 $UserName 是否已加入 $RDPGroup 群組中?" -ForegroundColor Green
If ((Get-LocalGroupMember -Group $RDPGroup -Member $UserName -ErrorAction SilentlyContinue)) {
Write-Warning "aplogger 帳號已加入 $RDPGroup 群組中。"
} Else {
Write-Host "將 aplogger 加入 $RDPGroup 群組中..."
AddToRDPGroup $UserName
}
# 變更 AP log 路徑的存取權限 (讓 aplogger 也能讀取)
Write-Host "===> 變更 AP log 路徑($ApLogPath)的存取權限..." -ForegroundColor Green
ChangeApLogPathACL $UserName $ApLogPath
Get-Acl $ApLogPath | Format-List
# 更新系統 hosts 檔(加入 AP Log 分析系統的 hosts)
# TODO:
# 1. 檢查是否系統 hosts 是否已經有加入過了?
# 2. 檢查 $Hosts 是否存在?
Write-Host "==> 修改系統 hosts 檔($SystemHostsPath)..." -ForegroundColor Green
Add-Content -Path $SystemHostsPath -Value "`n`n", (Get-Content -Path $APLogHostFilePath)
Write-Host "==> 檢查是否已安裝 Java?" -ForegroundColor Green
If (CheckJavaInstalled) {
Write-Warning "Java 已經安裝"
# 檢查是否為 Java 8 以上版本?
Write-Host "==> 檢查是否為 Java 8 以上版本?" -ForegroundColor Green
$JavaVersion = GetJavaVersion
Write-Host $JavaVersion
If (CheckJava8OrAbove($javaVersion)) {
# 檢查 $LogstashHomePath 目錄是否已經存在?
Write-Host "==> 檢查 $LogstashHomePath 目錄是否已經存在?" -ForegroundColor Green
If (Test-Path $LogstashHomePath) {
Write-Warning "$LogstashHomePath 目錄已存在。"
} Else {
Write-Host "開始解壓縮 Logstash,請稍候...."
Unzip $LogstashZipFilePath $WorkingPath
}
# 設定 %PATH%
AddLogstashBinToEnvPath $LogstashHomePath
# 將 Logstash 設定為服務
Write-Host "==> 檢查是 $ServiceName 是否服務已存在?" -ForegroundColor Green
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
If ($Service) {
Write-Warning "$ServiceName 服務已存在。"
} Else {
# https://blogs.msdn.microsoft.com/koteshb/2010/02/12/powershell-how-to-create-a-pscredential-object/
# https://blogs.technet.microsoft.com/gary/2009/07/23/creating-a-ps-credential-from-a-clear-text-password-in-powershell/
$BinaryPath = $LogstashHomePath + "\bin\logstash.bat -f " + $WorkingPath + "\logstash.conf"
$secpasswd = ConvertTo-SecureString $Password -AsPlainText -Force
$Credential = New-Object System.Management.Automation.PSCredential("$env:COMPUTERNAME\$UserName", $secpasswd)
New-Service -Name $ServiceName -DisplayName $ServiceName -Description "$ServiceName service." -BinaryPathName $BinaryPath -Credential $Credential
}
$Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
# 啟動服務
Write-Host "==> 啟動 $ServiceName 服務..." -ForegroundColor Green
$ConfirmStart = Start-Service -InputObject $Service -Confirm -PassThru | Format-Table
# 查看 Logstash 的 logs
If ($ConfirmStart) {
Write-Host "==> 查看 Logstash 的 logs" -ForegroundColor Green
Get-Content -Path $($LogstashHomePath + "\logs\logstash-plain.log") -Wait
}
} Else {
Write-Error "請安裝 Java 8 以上的版本!"
}
} Else {
Write-Error "尚未安裝 Java!請先安裝 Java 8 或以上版本。"
}
Write-Warning "==> 本程式即將於 60 秒後結束..."
Start-Sleep 60