PowerShell Skript erstellt

This commit is contained in:
2026-04-09 15:17:03 +02:00
parent 78f1d920f3
commit 46b1db68c9
2 changed files with 525 additions and 0 deletions

View File

@@ -0,0 +1,452 @@
#Requires -Version 5.1
param(
[switch]$SkipSelfUpdateCheck
)
<#
.SYNOPSIS
KANBAN-Rational-Druck mit Self-Update, Private-Config-Ladung, SQL-Abfrage und n8n-Webhook.
.DESCRIPTION
Ablauf:
1. Optional Self-Update aus dem Public-Repo
2. AZ_GITHUB_TOKEN prüfen
3. Private Konfiguration aus dem Private-Repo laden
4. Genau eine Datei aus Input-Ordner lesen
5. Dateiname ohne Endung als Belegnummer verwenden
6. SQL-Abfrage ausführen
7. Ergebnis an n8n senden
8. Logging in Datei
.NOTES
Repo: AZ-PowerShell-Pub
#>
$ErrorActionPreference = 'Stop'
$script:ScriptName = 'KANBAN-Rational-Druck'
$script:PublicRawUrl = 'https://git.az-gruppe.com/AZ-Intec-GmbH/AZ-PowerShell-Pub/raw/branch/main/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1'
$script:PrivateRawUrl = 'https://git.az-gruppe.com/AZ-Intec-GmbH/AZ-PowerShell-Prv/raw/branch/main/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1'
$script:LogFilePath = $null
#region Hilfsfunktionen
function Write-ConsoleLog
{
param(
[Parameter(Mandatory = $true)]
[string]$Message,
[ValidateSet('INFO', 'WARN', 'ERROR', 'SUCCESS')]
[string]$Level = 'INFO'
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = "[$timestamp] [$Level] $Message"
Write-Host $line
}
function Write-Log
{
param(
[Parameter(Mandatory = $true)]
[string]$Message,
[ValidateSet('INFO', 'WARN', 'ERROR', 'SUCCESS')]
[string]$Level = 'INFO'
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = "[$timestamp] [$Level] $Message"
Write-Host $line
if ($script:LogFilePath)
{
Add-Content -Path $script:LogFilePath -Value $line -Encoding UTF8
}
}
function Get-StringHashSha256
{
param(
[Parameter(Mandatory = $true)]
[string]$Text
)
$sha = [System.Security.Cryptography.SHA256]::Create()
try
{
$bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)
$hashBytes = $sha.ComputeHash($bytes)
return ([System.BitConverter]::ToString($hashBytes)).Replace('-', '')
} finally
{
$sha.Dispose()
}
}
function Test-AzGithubToken
{
if ([string]::IsNullOrWhiteSpace($env:AZ_GITHUB_TOKEN))
{
throw "❌ Umgebungsvariable AZ_GITHUB_TOKEN ist nicht gesetzt."
}
}
function Start-SelfUpdateIfNeeded
{
param(
[Parameter(Mandatory = $true)]
[string]$RemoteScriptUrl
)
if ($SkipSelfUpdateCheck)
{
Write-ConsoleLog "Self-Update-Prüfung wurde übersprungen (Restart nach Update)." "INFO"
return
}
$localScriptPath = $MyInvocation.MyCommand.Path
if ([string]::IsNullOrWhiteSpace($localScriptPath))
{
throw "❌ Lokaler Skriptpfad konnte nicht ermittelt werden."
}
Write-ConsoleLog "Prüfe auf neue Public-Version aus Git..." "INFO"
try
{
$remoteResponse = Invoke-WebRequest -Uri $RemoteScriptUrl -UseBasicParsing
$remoteContent = $remoteResponse.Content
if ([string]::IsNullOrWhiteSpace($remoteContent))
{
throw "Remote-Skriptinhalt ist leer."
}
$localContent = Get-Content -Path $localScriptPath -Raw -Encoding UTF8
$localHash = Get-StringHashSha256 -Text $localContent
$remoteHash = Get-StringHashSha256 -Text $remoteContent
if ($localHash -eq $remoteHash)
{
Write-ConsoleLog "✓ Public-Skript ist bereits aktuell." "SUCCESS"
return
}
Write-ConsoleLog "⚠ Neue Public-Version gefunden. Aktualisiere lokales Skript..." "WARN"
$tempUpdatedScript = Join-Path $env:TEMP ("{0}-{1}.ps1" -f $script:ScriptName, [guid]::NewGuid().ToString())
Set-Content -Path $tempUpdatedScript -Value $remoteContent -Encoding UTF8
Copy-Item -Path $tempUpdatedScript -Destination $localScriptPath -Force
Write-ConsoleLog "✓ Lokales Public-Skript wurde aktualisiert." "SUCCESS"
Write-ConsoleLog "Starte aktualisierte Version neu..." "INFO"
$argumentList = @(
'-ExecutionPolicy', 'Bypass',
'-File', ('"{0}"' -f $localScriptPath),
'-SkipSelfUpdateCheck'
)
Start-Process -FilePath 'powershell.exe' -ArgumentList $argumentList | Out-Null
Write-ConsoleLog "Aktuelle Instanz wird beendet, damit die neue Version ausgeführt wird." "INFO"
exit 0
} catch
{
throw "❌ Fehler bei der Self-Update-Prüfung des Public-Skripts: $($_.Exception.Message)"
}
}
function Get-PrivateConfig
{
param(
[Parameter(Mandatory = $true)]
[string]$RawConfigUrl
)
$token = $env:AZ_GITHUB_TOKEN
$headers = @{
Authorization = "token $token"
}
Write-ConsoleLog "Lade private Konfiguration aus dem Private-Repo..." "INFO"
try
{
$response = Invoke-WebRequest -Uri $RawConfigUrl -Headers $headers -UseBasicParsing
$content = $response.Content
$tempFile = Join-Path $env:TEMP ("KANBAN-Rational-Druck-private-{0}.ps1" -f ([guid]::NewGuid().ToString()))
Set-Content -Path $tempFile -Value $content -Encoding UTF8
. $tempFile
Remove-Item -Path $tempFile -Force -ErrorAction SilentlyContinue
if (-not $script:PrivateConfig)
{
throw "Private Konfiguration wurde geladen, aber `$script:PrivateConfig ist nicht gesetzt."
}
return $script:PrivateConfig
} catch
{
throw "❌ Fehler beim Laden der privaten Konfiguration: $($_.Exception.Message)"
}
}
function Test-RequiredConfig
{
param(
[Parameter(Mandatory = $true)]
[hashtable]$Config
)
$requiredKeys = @(
'InputFolder',
'LogFolder',
'WebhookUrl',
'SqlServer',
'SqlDatabase',
'SqlUser',
'SqlPassword',
'SqlQuery',
'SqlParameterName'
)
foreach ($key in $requiredKeys)
{
if (-not $Config.ContainsKey($key) -or [string]::IsNullOrWhiteSpace([string]$Config[$key]))
{
throw "❌ Fehlende Konfiguration: '$key'"
}
}
}
function Get-SingleInputFile
{
param(
[Parameter(Mandatory = $true)]
[string]$InputFolder
)
if (-not (Test-Path -Path $InputFolder))
{
throw "❌ Input-Ordner existiert nicht: $InputFolder"
}
$files = Get-ChildItem -Path $InputFolder -File
if ($files.Count -eq 0)
{
throw "❌ Keine Datei im Input-Ordner gefunden: $InputFolder"
}
if ($files.Count -gt 1)
{
throw "❌ Es wurden mehrere Dateien gefunden. Erwartet wird genau eine Datei im Ordner: $InputFolder"
}
return $files[0]
}
$SqlPasswordEncode = [System.Text.Encoding]::UTF8.GetString(
[System.Convert]::FromBase64String($Config.SqlPassword)
)
function Invoke-SqlTopOneQuery
{
param(
[Parameter(Mandatory = $true)]
[hashtable]$Config,
[Parameter(Mandatory = $true)]
[string]$Belegnummer
)
Write-Log "Starte SQL-Abfrage mit Parameter $($Config.SqlParameterName) = '$Belegnummer'..." "INFO"
$connectionString = "Server={0};Database={1};User ID={2};Password={3};" -f `
$Config.SqlServer,
$Config.SqlDatabase,
$Config.SqlUser,
$SqlPasswordEncode
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
try
{
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = $Config.SqlQuery
$command.CommandTimeout = 60
[void]$command.Parameters.AddWithValue($Config.SqlParameterName, $Belegnummer)
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $command
$dataTable = New-Object System.Data.DataTable
[void]$adapter.Fill($dataTable)
if ($dataTable.Rows.Count -eq 0)
{
Write-Log "⚠ Kein SQL-Datensatz gefunden." "WARN"
return $null
}
$row = $dataTable.Rows[0]
$result = [ordered]@{}
foreach ($column in $dataTable.Columns)
{
$value = $row[$column.ColumnName]
if ($value -eq [System.DBNull]::Value)
{
$result[$column.ColumnName] = $null
} else
{
$result[$column.ColumnName] = $value
}
}
Write-Log "✓ SQL-Datensatz erfolgreich gelesen." "SUCCESS"
return [pscustomobject]$result
} catch
{
throw "❌ Fehler bei SQL-Abfrage: $($_.Exception.Message)"
} finally
{
if ($connection.State -ne [System.Data.ConnectionState]::Closed)
{
$connection.Close()
}
$connection.Dispose()
}
}
function Send-N8nWebhook
{
param(
[Parameter(Mandatory = $true)]
[string]$WebhookUrl,
[Parameter(Mandatory = $true)]
[hashtable]$Payload
)
Write-Log "Sende Payload an n8n-Webhook..." "INFO"
try
{
$json = $Payload | ConvertTo-Json -Depth 10
$null = Invoke-RestMethod -Uri $WebhookUrl -Method Post -ContentType 'application/json' -Body $json
Write-Log "✓ Webhook erfolgreich gesendet." "SUCCESS"
} catch
{
throw "❌ Fehler beim Senden des Webhooks: $($_.Exception.Message)"
}
}
#endregion
#region Hauptlogik
try
{
Start-SelfUpdateIfNeeded -RemoteScriptUrl $script:PublicRawUrl
Test-AzGithubToken
$config = Get-PrivateConfig -RawConfigUrl $script:PrivateRawUrl
Test-RequiredConfig -Config $config
if (-not (Test-Path -Path $config.LogFolder))
{
New-Item -Path $config.LogFolder -ItemType Directory -Force | Out-Null
}
$script:LogFilePath = Join-Path $config.LogFolder ("KANBAN-Rational-Druck_{0}.log" -f (Get-Date -Format 'yyyy-MM-dd'))
Write-Log "Starte Skriptausführung KANBAN-Rational-Druck..." "INFO"
$inputFile = Get-SingleInputFile -InputFolder $config.InputFolder
$fileName = $inputFile.Name
$fileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($inputFile.Name)
$fileFullPath = $inputFile.FullName
$fileExtension = $inputFile.Extension
Write-Log "Gefundene Datei: $fileName" "INFO"
Write-Log "Extrahierte Belegnummer: $fileBaseName" "INFO"
$sqlResult = $null
$sqlFound = $false
$status = 'success'
$message = 'SQL-Datensatz gefunden'
try
{
$sqlResult = Invoke-SqlTopOneQuery -Config $config -Belegnummer $fileBaseName
if ($null -eq $sqlResult)
{
$sqlFound = $false
$status = 'warning'
$message = 'Kein SQL-Datensatz gefunden'
} else
{
$sqlFound = $true
}
} catch
{
Write-Log $_.Exception.Message "ERROR"
$sqlFound = $false
$status = 'error'
$message = $_.Exception.Message
}
$payload = [ordered]@{
status = $status
message = $message
fileName = $fileName
fileBaseName = $fileBaseName
fileFullPath = $fileFullPath
fileExtension = $fileExtension
processedAt = (Get-Date).ToString('s')
sqlFound = $sqlFound
sqlResult = $sqlResult
}
Send-N8nWebhook -WebhookUrl $config.WebhookUrl -Payload $payload
Write-Log "Skriptausführung abgeschlossen." "SUCCESS"
} catch
{
try
{
if (-not $script:LogFilePath)
{
$fallbackFolder = Join-Path $env:TEMP 'KANBAN-Rational-Druck'
if (-not (Test-Path $fallbackFolder))
{
New-Item -Path $fallbackFolder -ItemType Directory -Force | Out-Null
}
$script:LogFilePath = Join-Path $fallbackFolder ("KANBAN-Rational-Druck_{0}.log" -f (Get-Date -Format 'yyyy-MM-dd'))
}
Write-Log $_.Exception.Message "ERROR"
} catch
{
Write-Host "❌ Kritischer Fehler: $($_.Exception.Message)"
}
exit 1
}
#endregion

View File

@@ -0,0 +1,73 @@
# KANBAN-Rational-Druck
## Zweck
Dieses Skript verarbeitet genau eine Datei aus einem definierten Eingangsordner und nutzt den Dateinamen ohne Endung als Suchwert für eine MS-SQL-Abfrage.
Beispiel:
- Datei: `2026-1234567.txt`
- Suchwert für SQL: `2026-1234567`
Anschließend wird das Ergebnis per HTTP POST an einen n8n-Webhook gesendet.
---
## Funktionsweise
1. Prüft, ob `AZ_GITHUB_TOKEN` gesetzt ist
2. Lädt die private Konfiguration aus dem Repo `AZ-PowerShell-Prv`
3. Liest genau eine Datei aus dem Input-Ordner
4. Extrahiert den Dateinamen ohne Endung
5. Führt eine parametrisierte SQL-Abfrage aus
6. Sendet die Daten an n8n
7. Schreibt Logs in eine Logdatei
---
## Repo-Pfade
### Public
`AZ-PowerShell-Pub/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1`
### Private
`AZ-PowerShell-Prv/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1`
Raw-URL für Private-Datei:
`https://git.az-gruppe.com/AZ-Intec-GmbH/AZ-PowerShell-Prv/raw/branch/main/scripts/KANBAN-Rational-Druck/KANBAN-Rational-Druck.ps1`
---
## Voraussetzungen
- Windows
- PowerShell 5.1
- Zugriff auf den Gitea-Server
- `AZ_GITHUB_TOKEN` lokal als Umgebungsvariable gesetzt
- Netzwerkzugriff auf:
- Gitea
- MS-SQL Server
- n8n Webhook
---
## Benötigte Konfiguration im Private-Repo
Folgende Werte müssen in der privaten Datei gepflegt werden:
- `InputFolder`
- `LogFolder`
- `WebhookUrl`
- `SqlServer`
- `SqlDatabase`
- `SqlUser`
- `SqlPassword`
- `SqlQuery`
- `SqlParameterName`
---
## Beispiel-Aufruf
powershell.exe -ExecutionPolicy Bypass -File .\KANBAN-Rational-Druck.ps1