SUVI Problem mit Powershell Script

Hallo zusammen,

ich habe Probleme mit einem SUVI Task. Dieser soll ein Powershell-Script ausführen, welches dann Dokumente im Work-Verzeichnis von Enaio bearbeitet. Wenn ich das Script von Hand mit dem User ausführe, welcher auch im SUVI Dienst hinterlegt ist, funktioniert es Einwandfrei. Starte ich das Script aber über SUVI starte, läuft er auf einen Fehler. Zum Test habe ich es auch in die Windows Aufgabenplanung eingebunden und da funktioniert es :-/ Andere SUVI Task, in denen ich Powershell Scripte aufrufe und die auf auf Freigaben Dateien löschen etc. funktionieren Problemlos. Was macht SUVI hier anders?

Mein SUVI Task sieht so aus:

{
    "type": "schedule",
    "exec": "system",
    "schedule": "0 2 * * *",
    "args": [
        "powershell.exe", "-File", "C:\\SUVI\\apps\\ServerUpdateInfos\\UpdateInfoExcelProcess.ps1"
    ],
    "cwd": "C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0",
    "env": {"KONFIG_PFAD": "C:\\SUVI/config","SERVICE_NAME": "ProcessUpdateInfos", "LOG_PFAD": "C:\\SUVI/log", "LOG_LEVEL": "INFO"},
    "info": "## Trage Daten aus Updateinfo.csv in Doumentationen in enaio"
}

Das Powershell Script:

# Pfade zu den CSV-Dateien
$updateInfoCsvPath = "C:\SUVI\apps\ServerUpdateInfos\updateinfo.csv"
$matchingCsvPath = "C:\SUVI\apps\ServerUpdateInfos\Matching.csv"

$sendTo = "Test@kreis-reutlingen.de"

# Importiere die CSV-Dateien
try {
    $updateInfos = Import-Csv -Path $updateInfoCsvPath -Delimiter ";" -Header "ComputerName", "InstallDate", "Description", "UpdateType", "KBArticle", "User"
    $matchingData = Import-Csv -Path $matchingCsvPath -Delimiter ";" -Header "ComputerName", "ExcelFilePath"
}
catch {
    Write-Error "Fehler beim Importieren der CSV-Dateien: $($_.Exception.Message)"
    exit
}

# Initialize an empty array to store the output
$outputList = @() 
# Schleife durch die Update-Informationen
foreach ($updateInfo in $updateInfos) {
    
    # Finde den passenden Eintrag in der Matching-Datei
    $matchingEntry = $matchingData | Where-Object { $_.ComputerName -eq $updateInfo.ComputerName }

    $computerName = $updateInfo.ComputerName
    $computer = Get-ADComputer -Identity $computerName -Properties Description
    $ComputerDescription = $computer.Description
    Write-Host ('Starte Verarbeitung von {0} {1}' -f $updateInfo.ComputerName, $updateInfo.Description)
    # Überprüfe, ob ein passender Eintrag gefunden wurde
    if ($matchingEntry) {
        $excelFilePath = $matchingEntry.ExcelFilePath

        # Create a formatted string for the current update info
        
        $outputString = "$($updateInfo.ComputerName) | Description: $($ComputerDescription) | Install Date: $($updateInfo.InstallDate) | Update Type: $($updateInfo.UpdateType) | KB Article: $($updateInfo.KBArticle) | User: $($updateInfo.User)"
        $outputList += $outputString

        # Excel-Automatisierung
        try {
            # Erstelle eine Excel-Anwendung
            $excel = New-Object -ComObject Excel.Application
            $excel.Visible = $false  # Excel im Hintergrund ausführen
            $excel.DisplayAlerts = $false # Keine Warnmeldungen anzeigen

            # Öffne die Excel-Datei
            $workbook = $excel.Workbooks.Open($excelFilePath)
            $worksheet = $workbook.Worksheets.Item(1) # Greife auf das erste Arbeitsblatt zu

            # **Verschiebe Zeile 12 und alle darunter liegenden Zeilen um eine Zeile nach unten**
            if ($worksheet.UsedRange.Rows.Count -ge 12) {
                $worksheet.Rows("12:12").Insert()
            }
            else {
                Write-Warning "Row 12 does not exist in the worksheet. Skipping the row insertion."
            }

            # **Schreibe Update-Informationen in die Zeile 12**
            $worksheet.Cells.Item(12, 1).Value2 = $updateInfo.InstallDate    # Spalte A
            $worksheet.Cells.Item(12, 2).Value2 = $updateInfo.Description  # Spalte B
            $worksheet.Cells.Item(12, 3).Value2 = $updateInfo.UpdateType    # Spalte C
            $worksheet.Cells.Item(12, 4).Value2 = $updateInfo.KBArticle    # Spalte D
            $worksheet.Cells.Item(12, 5).Value2 = $updateInfo.User          # Spalte E


            # **Schreibe Datum in B3 und B4**
            $installDate = Get-Date $updateInfo.InstallDate
            $worksheet.Range("B3").Value2 = $installDate.ToString("dd.MM.yyyy") # Formatierung als dd.MM.yyyy
            $worksheet.Range("B4").Value2 = $installDate.AddYears(1).ToString("dd.MM.yyyy") # Datum + 1 Jahr

            # Speichern und Schließen
            $workbook.Save()
            $workbook.Close()
            $excel.Quit()

            # Aufräumen der COM-Objekte
            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($worksheet)
            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($workbook)
            [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)
            Remove-Variable excel, workbook, worksheet -ErrorAction SilentlyContinue # Variablen entfernen
            [gc]::collect() # Garbage Collection aufrufen
            [gc]::WaitForPendingFinalizers() # Warten bis Finalizer durchgelaufen sind

            Write-Host "Update erfolgreich in '$excelFilePath' geschrieben."
        }
        catch {
            $outputString = "$($updateInfo.ComputerName) | KEINE PASSENDE DATEI GEFUNDEN ZU DEM EINRTAG, BITTE IN MATCHING LISTE KORRIGIEREN."
            $outputList += $outputString
            # Sicherstellen, dass Excel geschlossen wird, auch wenn ein Fehler auftritt
            if ($workbook) {
                $workbook.Close($false) # Änderungen nicht speichern, falls Fehler aufgetreten sind
                [System.Runtime.InteropServices.Marshal]::ReleaseComObject($workbook)
            }
            if ($excel) {
                $excel.Quit()
                [System.Runtime.InteropServices.Marshal]::ReleaseComObject($excel)
            }
            Remove-Variable excel, workbook, worksheet -ErrorAction SilentlyContinue # Variablen entfernen
            [gc]::collect() # Garbage Collection aufrufen
            [gc]::WaitForPendingFinalizers() # Warten bis Finalizer durchgelaufen sind
        }
    }
    else {
        Write-host "Kein passender Eintrag für Computer '$($updateInfo.ComputerName)' in der Matching-Datei gefunden."
        write-host $excelFilePath
        # Create a formatted string for the current update info
        $outputString = "$($updateInfo.ComputerName) | Description: $($Description) | Install Date: $($updateInfo.InstallDate) | Update Type: $($updateInfo.UpdateType) | KB Article: $($updateInfo.KBArticle) | User: $($updateInfo.User) $newline"
        # Add the formatted string to the output list
        $outputList += $outputString
        $outputString = "$($updateInfo.ComputerName) | KEINE PASSENDE EINTRAG GEFUNDEN, BITTE IN MATCHING LISTE EINTRAGEN."
        $outputList += $outputString
    }
}

Write-Host "Skript abgeschlossen."
# === Umbenennen und Verschieben der CSV-Datei ===

# Erstelle den Pfad zum Backup-Ordner (wenn er nicht existiert)
$backupFolderPath = Join-Path -Path (Split-Path -Path $updateInfoCsvPath) -ChildPath "Backup"
if (!(Test-Path -Path $backupFolderPath -PathType Container)) {
    try {
        New-Item -ItemType Directory -Path $backupFolderPath -ErrorAction Stop
        Write-Host "Backup-Ordner '$backupFolderPath' erstellt."
    }
    catch {
        Write-Error "Fehler beim Erstellen des Backup-Ordners: $($_.Exception.Message)"
    }
}

# Erstelle den neuen Dateinamen mit dem aktuellen Datum als Präfix
$currentDate = Get-Date -Format "yyyyMMdd"
$originalFileName = Split-Path -Path $updateInfoCsvPath -Leaf
$newFileName = "$($currentDate)_$originalFileName"
$newFilePath = Join-Path -Path $backupFolderPath -ChildPath $newFileName

# Verschiebe die Datei in den Backup-Ordner und benenne sie um
try {
    Move-Item -Path $updateInfoCsvPath -Destination $newFilePath -ErrorAction Stop
    Write-Host "Datei '$originalFileName' wurde umbenannt in '$newFileName' und nach '$backupFolderPath' verschoben."
}
catch {
    Write-Error "Fehler beim Umbenennen und Verschieben der Datei: $($_.Exception.Message)"
}
if ($outputList) {
    $emailBody = ($outputList -join "<br>`n")
    start-LRASendMail `
        -Header 'Auf folgenden Servern wurden seit gestern Updates installiert.' `
        -MailAddress  $sendTo `
        -Subject 'Server Updates' `
        -Body $emailBody
    Write-Host 'E-Mail gesendet'
}
Write-Host 'Verarbeitung beendet'

Hallo @Johannes-Schmidt, danke für die spannende Frage. Mein erster Reflex wäre die Excel-Automatisierung, da der Prozess allenfalls keinen Desktop-Zugang hat. Darf ich noch Folgendes nachhaken:

  • Wie ist suvi installiert, als Windows-Service?
  • Gibt es eine Fehlermeldung bzw. einen Logeintrag im suvi, der den Fehler zeigt?
1 „Gefällt mir“

Hallo @rk ,

vielen Dank für die schnelle Antwort!
Suvi ist als Dienst in Windows installiert und im SUVI-Log wird leider kein Fehler erkannt. Das Script läuft immer in den catch, nachdem er versucht hat die Excel zu bearbeiten. In meine Mail steht dann | KEINE PASSENDE DATEI GEFUNDEN ZU DEM EINRTAG, BITTE IN MATCHING LISTE KORRIGIEREN.

1 „Gefällt mir“

Okay, da braucht es, glaube ich, einen längeren Atem beim Debuggen. Ich würde vorschlagen, das try-Statement Stück für Stück (oder in praktischen Schritten) nach unten zu verschieben, bis das Fehlerbild sich ändert.

Ich würde dabei zwei Dinge hoffen:

  • Wir finden heraus, welche Zeile den Fehler auslöst.
  • Ggf. sehen wir im Tasklog in suvi den Fehlertext.
1 „Gefällt mir“

Das war glaube ich ein guter Ansatz, ich habe das Try in Zeile 57 auf deinem Screenshot geschoben und es kommt folgender Fehler in SUVI:
(ich habe nur den Pfad hier geändert, dieser passt aber :slight_smile: )

ProcessUpdateInfos 2025-06-25 08:53:03 info Starte Verarbeitung von CMP03201S Windows Update wurde installiert
ProcessUpdateInfos 2025-06-25 08:53:04 error Microsoft Excel kann auf die Datei ‚PFAD…\WORK\04\2C\FB\00BCB4FB.xlsx‘ nicht
ProcessUpdateInfos 2025-06-25 08:53:04 error zugreifen. Dies kann mehrere Gr�nde haben:
ProcessUpdateInfos 2025-06-25 08:53:04 error a Der Name des Dokuments oder der Pfad ist nicht vorhanden.
ProcessUpdateInfos 2025-06-25 08:53:04 error a Das Dokument wird von einem anderen Programm verwendet.
ProcessUpdateInfos 2025-06-25 08:53:04 error a Der Name der Arbeitsmappe, die gespeichert werden soll, ist identisch zu dem Namen eines anderen
ProcessUpdateInfos 2025-06-25 08:53:04 error Dokuments, welches schreibgesch�tzt ist.
ProcessUpdateInfos 2025-06-25 08:53:04 error In C:\SUVI\apps\ServerUpdateInfos\UpdateInfoExcelProcess.ps1:55 Zeichen:13
1 „Gefällt mir“

Cool. Könntest Du Dir vorstellen, den Filename nochmals auszugeben, sodass wir das kontrollieren können?

Ich habe leider keine tiefe PowerShell-Erfahrung, aber mich hat das hier stutzig gemacht:

Log

1 „Gefällt mir“

(Hinweis am Rande: unter anderem, da enaio Prüfsummen für alle Dateien führt, ist es IMHO nicht „erlaubt“ Dokumente direkt im Work-Verzeichnis zu bearbeiten. Vielmehr müsste die Datei geladen und per API aktualisiert werden.)

1 „Gefällt mir“

Das habe ich bewusst von Hand geändert, bevor ich es hier gepostet hatte :smiley: Der Pfad sieht ungefähr so aus:
\Server\enaio\server\WORK\04\2C\FB\00BCB4FB.xlsx
Die Datei ist darunter auch zu finden. Wenn ich das Script manuell starte, auch in dem Userkontext des SUVI-Dienste-Users, findet er diese auch und kann sie bearbeiten.

Die Prüfsummen haben wir in enaio nicht aktiviert…

Das gibt Sinn :smile:

Bleibt also die Frage, was den Prozess daran hinter könnte. Eventuell mal versuchen, die Datei vor der Bearbeitung nach C:\Temp zu kopieren? Das wäre aus meiner Sicht eh gut, wegen:

1 „Gefällt mir“

Soooooo…

Ich habe es jetzt nochmal alles mit einem Kollegen angeschaut und analysiert. Eigentlich haben wir am Script, sowie den Dateien nichts geändert aber plötzlich funktioniert es. :face_with_peeking_eye:
Evtl. hat das explizite öffnen der Pfade und Dateien im ausführenden User etwas bewirkt?

Vielen Dank Roland für die schnelle Reaktion und die Hilfe beim analysieren!

1 „Gefällt mir“