446 lines
15 KiB
PowerShell
446 lines
15 KiB
PowerShell
#############################################################################################
|
|
# This script downloads, if necessary, Promtail and its WinSW (Windows Service wrapper),
|
|
# creates default configuration and creates Windows service.
|
|
# It's a decision based script.
|
|
#
|
|
# ↓ ↓ ↓ ↓ HELPER FUNCTIONS ↓ ↓ ↓ ↓
|
|
# ↓↓↓↓↓↓↓ PROCESSING CODE ↓↓↓↓↓↓↓
|
|
#############################################################################################
|
|
|
|
param(
|
|
## https://devblogs.microsoft.com/powershell/v2-custom-enums/ better, but limited to alphanumeric, so *DHCP would not work
|
|
[Parameter(Mandatory=$true, HelpMessage="Hotel site e.g. gb-bw-peartree-manchester, bikes, gb-ihg-holidayinn-birmingham")][String]$HotelGroup,
|
|
[Parameter(Mandatory=$true, HelpMessage="Hotel site e.g. operaapp, checkin, ")][ValidateSet('operaapp','checkin','hopapp')][String]$apptype,
|
|
[Parameter(Mandatory=$true, HelpMessage="Have you updated the following Group Policy? Computerconfig > windows settings > security settings > advanced audit policy config > object access > double click audit removable storage.(yes/no)")][ValidateSet('yes')][String]$gpochange
|
|
)
|
|
|
|
function Prompt-User {
|
|
param(
|
|
[string]$Prompt,
|
|
[object]$Default
|
|
)
|
|
|
|
if (-not [string]::IsNullOrEmpty($Default)) {
|
|
if ($Default -is [bool]) {
|
|
$Prompt += " [$(if ($Default) {'True'} else {'False'})]"
|
|
}
|
|
else {
|
|
$Prompt += " [$Default]"
|
|
}
|
|
}
|
|
|
|
$input = Read-Host -Prompt $Prompt
|
|
|
|
if ([string]::IsNullOrEmpty($input)) {
|
|
$input = $Default
|
|
}
|
|
else {
|
|
if ($Default -is [bool]) {
|
|
$input = [bool]::Parse($input)
|
|
}
|
|
elseif ($Default -is [int]) {
|
|
$input = [int]::Parse($input)
|
|
}
|
|
elseif ($Default -is [string]) {
|
|
# No conversion needed for string
|
|
}
|
|
else {
|
|
throw "Unsupported default value type: $($Default.GetType().FullName)"
|
|
}
|
|
|
|
if ($input.GetType() -ne $Default.GetType()) {
|
|
throw "Entered value type doesn't match default value type"
|
|
}
|
|
}
|
|
|
|
return $input
|
|
}
|
|
|
|
function Ensure-Directory {
|
|
param(
|
|
[string]$Path
|
|
)
|
|
|
|
if (-not [System.IO.Directory]::Exists($Path)) {
|
|
[System.IO.Directory]::CreateDirectory($Path) | Out-Null
|
|
}
|
|
}
|
|
|
|
function Download-File {
|
|
param (
|
|
# Url of file to download
|
|
[Parameter(Mandatory)]
|
|
[string]$URL,
|
|
|
|
# Parameter help description
|
|
[Parameter(Mandatory)]
|
|
[string]$File
|
|
)
|
|
Begin {
|
|
function Show-Progress {
|
|
param (
|
|
# Enter total value
|
|
[Parameter(Mandatory)]
|
|
[Single]$TotalValue,
|
|
|
|
# Enter current value
|
|
[Parameter(Mandatory)]
|
|
[Single]$CurrentValue,
|
|
|
|
# Enter custom progresstext
|
|
[Parameter(Mandatory)]
|
|
[string]$ProgressText,
|
|
|
|
# Enter value suffix
|
|
[Parameter()]
|
|
[string]$ValueSuffix,
|
|
|
|
# Enter bar lengh suffix
|
|
[Parameter()]
|
|
[int]$BarSize = 40,
|
|
|
|
# show complete bar
|
|
[Parameter()]
|
|
[switch]$Complete
|
|
)
|
|
|
|
# calc %
|
|
$percent = $CurrentValue / $TotalValue
|
|
$percentComplete = $percent * 100
|
|
if ($ValueSuffix) {
|
|
$ValueSuffix = " $ValueSuffix" # add space in front
|
|
}
|
|
if ($psISE) {
|
|
Write-Progress "$ProgressText $CurrentValue$ValueSuffix of $TotalValue$ValueSuffix" -id 0 -percentComplete $percentComplete
|
|
}
|
|
else {
|
|
# build progressbar with string function
|
|
$curBarSize = $BarSize * $percent
|
|
$progbar = ""
|
|
$progbar = $progbar.PadRight($curBarSize, [char]9608)
|
|
$progbar = $progbar.PadRight($BarSize, [char]9617)
|
|
|
|
if (!$Complete.IsPresent) {
|
|
Write-Host -NoNewLine "`r$ProgressText $progbar [ $($CurrentValue.ToString("#.###").PadLeft($TotalValue.ToString("#.###").Length))$ValueSuffix / $($TotalValue.ToString("#.###"))$ValueSuffix ] $($percentComplete.ToString("##0.00").PadLeft(6)) % complete"
|
|
}
|
|
else {
|
|
Write-Host -NoNewLine "`r$ProgressText $progbar [ $($TotalValue.ToString("#.###").PadLeft($TotalValue.ToString("#.###").Length))$ValueSuffix / $($TotalValue.ToString("#.###"))$ValueSuffix ] $($percentComplete.ToString("##0.00").PadLeft(6)) % complete"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Process {
|
|
try {
|
|
$storeEAP = $ErrorActionPreference
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
# invoke request
|
|
$request = [System.Net.HttpWebRequest]::Create($URL)
|
|
$response = $request.GetResponse()
|
|
|
|
if ($response.StatusCode -eq 401 -or $response.StatusCode -eq 403 -or $response.StatusCode -eq 404) {
|
|
throw "Remote file either doesn't exist, is unauthorized, or is forbidden for '$URL'."
|
|
}
|
|
|
|
if ($File -match '^\.\\') {
|
|
$File = Join-Path (Get-Location -PSProvider "FileSystem") ($File -Split '^\.')[1]
|
|
}
|
|
|
|
if ($File -and !(Split-Path $File)) {
|
|
$File = Join-Path (Get-Location -PSProvider "FileSystem") $File
|
|
}
|
|
|
|
if ($File) {
|
|
$fileDirectory = $([System.IO.Path]::GetDirectoryName($File))
|
|
if (!(Test-Path($fileDirectory))) {
|
|
[System.IO.Directory]::CreateDirectory($fileDirectory) | Out-Null
|
|
}
|
|
}
|
|
|
|
[long]$fullSize = $response.ContentLength
|
|
$fullSizeMB = $fullSize / 1024 / 1024
|
|
|
|
# define buffer
|
|
[byte[]]$buffer = new-object byte[] 1048576
|
|
[long]$total = [long]$count = 0
|
|
|
|
# create reader / writer
|
|
$reader = $response.GetResponseStream()
|
|
$writer = new-object System.IO.FileStream $File, "Create"
|
|
|
|
# start download
|
|
$finalBarCount = 0 #show final bar only one time
|
|
do {
|
|
|
|
$count = $reader.Read($buffer, 0, $buffer.Length)
|
|
|
|
$writer.Write($buffer, 0, $count)
|
|
|
|
$total += $count
|
|
$totalMB = $total / 1024 / 1024
|
|
|
|
if ($fullSize -gt 0) {
|
|
Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix "MB"
|
|
}
|
|
|
|
if ($total -eq $fullSize -and $count -eq 0 -and $finalBarCount -eq 0) {
|
|
Show-Progress -TotalValue $fullSizeMB -CurrentValue $totalMB -ProgressText "Downloading $($File.Name)" -ValueSuffix "MB" -Complete
|
|
$finalBarCount++
|
|
#Write-Host "$finalBarCount"
|
|
}
|
|
|
|
} while ($count -gt 0)
|
|
}
|
|
|
|
catch {
|
|
|
|
$ExeptionMsg = $_.Exception.Message
|
|
Write-Host "Download breaks with error : $ExeptionMsg"
|
|
}
|
|
|
|
finally {
|
|
# cleanup
|
|
if ($reader) { $reader.Close() }
|
|
if ($writer) { $writer.Flush(); $writer.Close() }
|
|
|
|
$ErrorActionPreference = $storeEAP
|
|
[GC]::Collect()
|
|
|
|
Write-Host
|
|
}
|
|
}
|
|
}
|
|
|
|
function New-DefaultConfig {
|
|
param (
|
|
# Parameter help description
|
|
[Parameter(Mandatory)]
|
|
[string]$fullConfigPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$runPath
|
|
)
|
|
|
|
Write-Output "Writing default config to $fullConfigPath"
|
|
|
|
$positionsFullpath = $runPath + "\positions.yaml"
|
|
|
|
$content = "
|
|
# 1. Update positions.yaml path
|
|
# 2. Update client's url - this is the url of Loki service - update or remove basic_auth
|
|
# 3. Update what logs should be scraped
|
|
|
|
|
|
server:
|
|
http_listen_port: 9081
|
|
grpc_listen_port: 0
|
|
|
|
positions:
|
|
filename: 'C:\ProgramData\Promtail\positions.txt'
|
|
|
|
clients:
|
|
- url: 'https://loki.futuresens.co.uk/loki/api/v1/push'
|
|
|
|
scrape_configs:
|
|
- job_name: testapptype
|
|
static_configs:
|
|
- targets:
|
|
- localhost
|
|
labels:
|
|
hotel: testhotel
|
|
job: testapptype
|
|
__path__: 'C:\Program Files\Self-Service Technology\Logs\testhotel.log'
|
|
- job_name: sst
|
|
static_configs:
|
|
- targets:
|
|
- localhost
|
|
labels:
|
|
hotel: testhotel
|
|
job: sst
|
|
__path__: 'C:\Program Files\Self-Service Technology\Logs\SST.Browser.log'
|
|
- job_name: windows-system
|
|
windows_events:
|
|
eventlog_name: 'Microsoft-Windows-DriverFrameworks-UserMode/Operational'
|
|
labels:
|
|
logsource: windows-eventlog
|
|
use_incoming_timestamp: true
|
|
bookmark_path: 'C:\ProgramData\Promtail\bookmark-system.xml'
|
|
exclude_event_data: true
|
|
exclude_user_data: true
|
|
locale: 1033
|
|
"
|
|
$content = $content.Replace('testhotel',"$HotelGroup")
|
|
$content = $content.Replace('testapptype',"$apptype")
|
|
Set-Content -Path $fullConfigPath -Value $content
|
|
}
|
|
|
|
function New-DefaultWinSWConfig {
|
|
param (
|
|
# Parameter help description
|
|
[Parameter(Mandatory)]
|
|
[string]$fullConfigPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$fullPromtailBinPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$fullPromtailConfigPath,
|
|
[Parameter(Mandatory)]
|
|
[string]$serviceName,
|
|
[Parameter(Mandatory)]
|
|
[string]$serviceDisplayName
|
|
)
|
|
|
|
Write-Output "Writing default WinSW config to $fullConfigPath"
|
|
|
|
$content = "
|
|
<!--
|
|
You can find more information about the configuration options here: https://github.com/kohsuke/winsw/blob/master/doc/xmlConfigFile.md
|
|
Full example: https://github.com/kohsuke/winsw/blob/master/examples/sample-allOptions.xml
|
|
-->
|
|
<service>
|
|
|
|
<!-- ID of the service. It should be unique across the Windows system-->
|
|
<id>$serviceName</id>
|
|
<!-- Display name of the service -->
|
|
<name>$serviceDisplayName</name>
|
|
<!-- Service description -->
|
|
<description>Starts a local Promtail service and scrapes logs according to configuration file: $fullPromtailConfigPath</description>
|
|
|
|
<!-- Path to the executable, which should be started -->
|
|
<executable>""$fullPromtailBinPath""</executable>
|
|
|
|
<arguments>--config.file=""$fullPromtailConfigPath""</arguments>
|
|
|
|
</service>
|
|
"
|
|
|
|
Set-Content -Path $fullConfigPath -Value $content
|
|
}
|
|
|
|
|
|
#############################################
|
|
# ↑ ↑ ↑ ↑ HELPER FUNCTIONS ↑ ↑ ↑ ↑
|
|
#
|
|
#
|
|
# ↓ ↓ ↓ ↓ PROCESSING CODE ↓ ↓ ↓ ↓
|
|
#############################################
|
|
|
|
|
|
Write-Warning -Message "This script creates a Window Service for Promtail log scraper. It is necessary to run it with Admin priviledges.
|
|
|
|
It can download necessary files from the Internet, but you can also put already downloaded files directly to proper directories."
|
|
|
|
## Below will enable event logs to allow the USB decetion logs.
|
|
|
|
$logDetails = Get-LogProperties 'Microsoft-Windows-DriverFrameworks-UserMode/Operational'
|
|
$logDetails.Enabled = $True
|
|
Set-LogProperties -LogDetails $logDetails
|
|
Get-LogProperties 'Microsoft-Windows-DriverFrameworks-UserMode/Operational'
|
|
|
|
$downloadUrl = "https://github.com/grafana/loki/releases/download/v2.9.5/promtail-windows-amd64.exe.zip"
|
|
$downloadWinSWUrl = "https://github.com/winsw/winsw/releases/download/v2.12.0/WinSW-x64.exe"
|
|
$winSWFilename = "WinSW-x64.exe"
|
|
$binFilename = "promtail-windows-amd64.exe"
|
|
$configFilename = "promtail.yml"
|
|
$winSWConfigFilename = "WinSW-x64.xml"
|
|
|
|
$runPath = Prompt-User -Prompt "Run directory" -Default "C:\Promtail"
|
|
$fullBinPath = Join-Path -Path $runPath -ChildPath $binFilename
|
|
$fullWinSWBinPath = Join-Path -Path $runPath -ChildPath $winSWFilename
|
|
$downloadWinSWPath = $runPath
|
|
|
|
$shouldDownloadPromtail = Prompt-User -Prompt "Should we download Promtail?" -Default $true
|
|
|
|
if ($shouldDownloadPromtail) {
|
|
$downloadUrl = Prompt-User -Prompt "Download url" -Default $downloadUrl
|
|
$downloadPath = Prompt-User -Prompt "Download directory" -Default $runPath
|
|
|
|
Ensure-Directory -Path $downloadPath
|
|
Ensure-Directory -Path 'c:\ProgramData\Promtail'
|
|
|
|
## Now create the positions files. NB Please create a Positions file for each Job.
|
|
|
|
New-Item -Path ”c:\ProgramData\Promtail\positions.txt” -ItemType File
|
|
New-Item -Path ”c:\ProgramData\Promtail\bookmark-system.xml” -ItemType File
|
|
|
|
$filename = $downloadUrl.Split("/")[-1]
|
|
$fullPath = Join-Path -Path $downloadPath -ChildPath $filename
|
|
|
|
Write-Host "Downloading archive..."
|
|
Download-File -URL $downloadUrl -File $fullPath
|
|
|
|
Write-Host "Expanding archive..."
|
|
Expand-Archive -LiteralPath $fullPath -DestinationPath $runPath -Force
|
|
}
|
|
else {
|
|
if (-not [System.IO.File]::Exists($fullBinPath)) {
|
|
throw "Could not find $fullBinPath"
|
|
}
|
|
}
|
|
|
|
$shouldCreateConfig = Prompt-User -Prompt "Create default promtail.yml config?" -Default $true
|
|
$configFullpath = Join-Path -Path $runPath -ChildPath $configFilename
|
|
|
|
if ($shouldCreateConfig) {
|
|
New-DefaultConfig -fullConfigPath $configFullpath -runPath $runPath
|
|
}
|
|
else {
|
|
$configFullpath = Prompt-User -Prompt "Promtail configuration file path" -Default $configFullpath
|
|
|
|
if (-not [System.IO.File]::Exists($configFullpath)) {
|
|
throw "Could not find $configFullpath"
|
|
}
|
|
}
|
|
|
|
$shouldCreateService = Prompt-User -Prompt "Create Promtail Windows service?" -Default $true
|
|
|
|
if ($shouldCreateService) {
|
|
|
|
$shouldDownloadWinSWUrl = Prompt-User -Prompt "Should we download Windows Service wrapper (WinSWUrl)?" -Default $true
|
|
|
|
if ($shouldDownloadWinSWUrl) {
|
|
$downloadUrl = Prompt-User -Prompt "Download url" -Default $downloadWinSWUrl
|
|
$downloadWinSWPath = Prompt-User -Prompt "Download directory" -Default $runPath
|
|
|
|
Ensure-Directory -Path $downloadPath
|
|
|
|
$filename = $winSWFilename
|
|
$fullWinSWBinPath = Join-Path -Path $downloadWinSWPath -ChildPath $filename
|
|
|
|
Write-Host "Downloading WinSW exe file..."
|
|
Download-File -URL $downloadUrl -File $fullWinSWBinPath
|
|
}
|
|
else {
|
|
if (-not [System.IO.File]::Exists($fullWinSWBinPath)) {
|
|
throw "Could not find $fullWinSWBinPath"
|
|
}
|
|
}
|
|
|
|
|
|
$winSWconfigFullpath = Join-Path -Path $downloadWinSWPath -ChildPath $winSWConfigFilename
|
|
$shouldCreateWinSWConfig = Prompt-User -Prompt "Create WinSW config as $winSWconfigFullpath ?" -Default $true
|
|
|
|
if ($shouldCreateWinSWConfig) {
|
|
$serviceName = Prompt-User -Prompt "Service name" -Default "Promtail"
|
|
$serviceDisplayName = Prompt-User -Prompt "Service name" -Default "Promtail Logs scraper"
|
|
|
|
New-DefaultWinSWConfig -fullConfigPath $winSWconfigFullpath -fullPromtailBinPath $fullBinPath -fullPromtailConfigPath $configFullpath -serviceName $serviceName -serviceDisplayName $serviceDisplayName
|
|
}
|
|
else {
|
|
if (-not [System.IO.File]::Exists($winSWconfigFullpath)) {
|
|
throw "Could not find $winSWconfigFullpath"
|
|
}
|
|
}
|
|
|
|
Write-Host "Installing Promtail Windows Service"
|
|
|
|
Start-Process -FilePath $fullWinSWBinPath -ArgumentList @("install") -NoNewWindow
|
|
|
|
Write-Host "Starting the Service"
|
|
|
|
start-sleep -Seconds 5
|
|
|
|
Start-Service Promtail
|
|
|
|
Set-ExecutionPolicy Restricted -Force
|
|
|
|
Write-Host "Reverting execution policy"
|
|
} |