Es ist keine schlechte Idee, die Kennwörter von Dienst- und Taskausführungskonten zu ändern. Vielfach sind diese nämlich mit einem, naja, Startkennwort ausgestattet, das nicht den hohen Sicherheitsanforderungen für solche Konten entspricht. Doch fast niemand traut sich ran, denn es gibt so eine tolle Ausrede: Wer weiß, wo dieses Konto überall genutzt wird, und nachher läuft da gar nichts mehr!
Aber so schwierig ist es gar nicht, so etwas sicher herauszufinden. Wie so oft, hilft auch hier eine Skriptlösung (Skript-Download findet sich am Ende des Artikels!). Betrachten wir dazu getrennt die Dienstkonten und die Konten, mit denen Geplante Tasks laufen.
Teil 1: Dienstkonten
Dienstkonten lassen sich gut mit WMI abfragen und dann in eine Datenbank schreiben. Alles, was man dazu benötigt, ist:
- Ein SQL Server (die Express Edition reicht hier vollkommen)
- Ein Konto, das auf die Server zugreifen darf, idealerweise mit Adminrechten
Auf dem SQL Server richtet man eine Datenbank beliebigen Namens ein, z.B. so:
CREATE DATABASE MeineAdminDB
Dann führt man folgendes Skript in einem Admintool für den SQL Server aus (SQL Server Management Studio, -Express oder einfach Ofarim):
USE MeineAdminDB
CREATE TABLE [dbo].[Win32Service] (
[idService] [int] IDENTITY (1, 1) NOT NULL ,
[Caption] [nvarchar] (255) COLLATE Latin1_General_CI_AS NULL ,
[Name] [nvarchar] (255) COLLATE Latin1_General_CI_AS NULL ,
[PathName] [ntext] COLLATE Latin1_General_CI_AS NULL ,
[StartMode] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[StartName] [nvarchar] (255) COLLATE Latin1_General_CI_AS NULL ,
[State] [varchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[SystemName] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL ,
[RowDate] [smalldatetime] NULL ,
[UserName] [nvarchar] (50) COLLATE Latin1_General_CI_AS NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Damit hat die Datenbank die nötige Tabelle, um die Daten aufzunehmen. Nun führt man das folgende Skript auf allen Servern aus, deren Dienste inventarisiert werden sollen. Eine Hilfestellung dazu gebe ich unter dem Code. Im Code ändere man in den Zeilen 61 und 62 den Namen des SQL-Servers und der Datenbank.
‚ ServiceInv.vbs
‚ Dienst-Mini-Inventarisierung über WMI
‚
‚ Version: 0.1
‚ Datum: 22.12.2005
‚ Autor: Nils Kaczenski (Vorname at Nachname .de)
‚ Letzte Änderungen:
‚
‚ Nils Kaczenski stellt dieses Skript ohne jede
‚ Gewährleistung zur Verfügung.
‚ Die Verwendung geschieht auf eigene Gefahr.
‚
‚ ““““““““““““““““““““““““““
Option Explicit
Dim colItems ‚
Dim dtmCompInDB ‚
Dim intGesamtzahl ‚
Dim intZeilenzahl ‚
Dim objArgs ‚
Dim objConn ‚
Dim objItem
Dim objRS ‚
Dim objWMIService
Dim objWSHNetwork ‚
Dim strBenutzer ‚
Dim strCaption ‚
Dim strComputer ‚
Dim strConn ‚
Dim strDatenbank ‚
Dim strDBServer ‚
Dim strLogmode ‚
Dim strName ‚
Dim strPathName ‚
Dim strSQL ‚
Dim strStartMode ‚
Dim strStartName ‚
Dim strState ‚ strLogmode = „error“say Now & „: Starte Dienst-Inventarisierung …“
On Error Resume Next
‚ Computernamen herausfinden
Set objWSHNetwork = CreateObject(„Wscript.Network“)
strComputer = objWSHNetwork.ComputerName
strBenutzer = objWSHNetwork.UserDomain & „\“ & objWSHNetwork.UserName
If checkit(„Fehler beim Zugriff auf WSHNetwork!“) Then WScript.Quit
‚ falls Computername per Argument übergeben, wird dieser genommen:
Set objArgs = WScript.Arguments
If objArgs.Count <> 0 Then
strComputer = objArgs(0)
End If
saydebug strComputer
‚ Datenbank-Daten
strDBServer = „SQL01“
strDatenbank = „MeineAdminDB“
‚ Objekte
Set objWMIService = GetObject(„winmgmts:\\“ & strComputer & „\root\cimv2“)
If checkit(„Fehler beim Zugriff auf WMI!“) Then WScript.Quit
Set objConn = CreateObject(„ADODB.Connection“)
Set objRS = CreateObject(„ADODB.Recordset“)
If checkit(„Fehler beim ADODB-Zugriff!“) Then WScript.Quit
‚ Datenbank-Verbindung
strConn = „Provider=SQLOLEDB;Integrated Security=SSPI;“ _
& „Initial Catalog=“ & strDatenbank _
& „;Data Source=“ & strDBServer
objConn.Open strConn
If checkit(„Fehler beim Verbinden mit der Datenbank!“) Then WScript.Quit
‚ prüfen, ob aktuelle Daten da sind
strSQL = „select max(RowDate) as datum from dbo.Win32Service where „ _
& „SystemName='“ & strComputer & „‚“
objRS.Open strSQL, objConn
If checkit(„Fehler beim Abfragen der Datenbank!“) Then WScript.Quit
dtmCompInDB = objRS(„Datum“)
If DateDiff(„d“, dtmCompInDB, Now) < 7 Then
‚ Daten sind aktuell – Skript verlassen
say „Vorhandene Daten sind aktuell.“
say „Beende Service-Inventarisierung.“
WScript.Quit
End If
objRS.Close
‚ vorhandene Daten dieses Rechners löschen
strSQL = „delete from Win32Service where SystemName='“ & strComputer & „‚“
objConn.Execute strSQL, intZeilenzahl
If checkit(„Fehler beim Löschen der Inventardaten!“) Then WScript.Quit
say intZeilenzahl & “ Zeilen aus der Datenbank gelöscht.“
‚ WMI-Daten auslesen
Set colItems = objWMIService.ExecQuery(„Select * from Win32_Service“,,48)
If checkit(„Fehler beim Auslesen der WMI-Daten!“) Then WScript.Quit
‚ WMI-Daten durchlaufen
For Each objItem In colItems
strCaption = objItem.Caption
strName = objItem.Name
strPathName = objItem.PathName
strStartMode = objItem.StartMode
strStartName = objItem.StartName
strState = objItem.State
If checkit(„Fehler beim Durchlaufen der WMI-Daten, Item „ & intGesamtzahl & „!“) Then WScript.Quit
‚ SQL-Kommando aufbauen
strSQL = „insert dbo.Win32Service („ _
& “ SystemName, Caption, [Name], PathName, StartMode,“ _
& “ StartName, State, RowDate, Username „ _
& „) values ( ‚“ _
& strComputer & „‚, ‚“ & strCaption & „‚, ‚“ & strName _
& „‚, ‚“ & strPathName & „‚, ‚“ & strStartMode _
& „‚, ‚“ & strStartName & „‚, ‚“ & strState & „‚, ‚“ _
& Now & „‚, ‚“ & strBenutzer & „‚) „
saydebug strSQL
‚ Daten in die Datenbank schreiben
objConn.Execute strSQL, intZeilenzahl
If checkit(„Fehler beim Schreiben in die Datenbank!“) Then WScript.Quit
intGesamtzahl = intGesamtzahl + intZeilenzahl
Next
objConn.Close
On Error Goto 0
say intGesamtzahl & “ Zeilen in die Datenbank geschrieben.“
say „Service-Inventarisierung ist fertig.“
Function checkit(strNachricht)
checkit = False
If Err.number <>0 Then
strNachricht = strNachricht & “ [„ & Err.description & “ („ & Err.number & „)]“
sayerror strNachricht
Err.clear
checkit=True
End If
End Function
‚ Universelle Ausgabefunktion
Sub say(s)
saynb s & VbCrLf
End Sub
‚ Ausgabe ohne Zeilenumbruch
Sub saynb(s)
WScript.echo s
End Sub
‚ Ausgabe als Fehlermeldung
Sub sayerror(s)
say „Fehler („ & Date & „, „ & Time & „): „ & s
End Sub
‚ Ausgabe als Warnung
Sub sayalert(s)
say „Warnung („ & Date & „, „ & Time & „): „ & s
End Sub
‚ Debug-Ausgabe
Sub saydebug(s)
If strLogmode = „debug“ Then
say s
End If
End Sub
Dieses Skript speichert man als „ServiceInv.vbs“ und ruft es folgendermaßen – mit ausreichenden Rechten ausgestattet – auf, um den Server „MeinServer“ zu inventarisieren:
cscript C:\Pfad\ServiceInv.vbs MeinServer
Dies lässt sich für alle Server gut über ein Batch erledigen (das man mit Excel erzeugen kann), das z.B. so aussieht:
cscript C:\Pfad\ServiceInv.VBS SERVER001
cscript C:\Pfad\ServiceInv.VBS SERVER002
cscript C:\Pfad\ServiceInv.VBS SERVER003
Sollte das Skript einen Server nicht per WMI erreichen, kann das mehrere Gründe haben: Der Server läuft gar nicht, eine Firewall hindert die WMI-Ansprache oder es gibt gar kein WMI auf der Maschine. Die meisten Windows-Server sollten aber klaglos ihre Daten ausgeben.
Hernach stehen die Daten zu den Dienstkonten in der Datenbank. Folgende SQL-Abfrage im SQL-Client (siehe oben) gibt eine schnelle Übersicht, welches Dienstkonto wo verwendet wird. Dabei blendet es die lokalen Standardkonten gleich aus, weil diese ja nicht von der Kennwortänderung betroffen sind:
-
SELECT StartName
-
, Caption
-
, SystemName
-
, StartMode
-
FROM Win32Service
-
WHERE StartName NOT IN (
-
‚LocalSystem‘
-
, ‚NT AUTHORITY\LocalService‘
-
, ‚NT AUTHORITY\NetworkService‘)
-
ORDER BY StartName, SystemName, Caption
Teil 2: Konten für Geplante Tasks
Mit den Geplanten Tasks ist es leider nicht so einfach, denn diese lassen sich nicht per WMI abfragen – jedenfalls wenn sie mit dem Taskplaner eingerichtet wurden. WMI kann nur die „alten“ AT-Tasks verarbeiten (die aber zu Recht niemand mehr will).
Hier hilft das Kommandozeilentool schtasks.exe, das auf Rechnern ab XP und Windows Server 2003 vorhanden ist. Gemeinsam mit psexec (von Sysinternals) wird eine brauchbare Lösung daraus. Man baue sich also (z.B. wieder mit Excel, siehe oben) ein Batch folgender Art:
„C:\Daten\psexec.exe“ \\SERVER001 schtasks /query /V /FO csv|find /i „Domain“
„C:\Daten\psexec.exe“ \\SERVER002 schtasks /query /V /FO csv|find /i „Domain“
„C:\Daten\psexec.exe“ \\SERVER003 schtasks /query /V /FO csv|find /i „Domain“
Oder man nutzt den Schalter /S des schtasks-Kommandos und gibt darüber gleich das zu befragende System an. Wichtig dabei: Das Konto, mit dem man das tut, benötigt administrative Rechte auf dem Zielsystem. Hat man das CMD-Fenster nicht gleich mit einem solchen Konto gestartet, so kann man per /U und /P auch noch den zu verwendenden Account und dessen Kennwort angeben.
schtasks /query /s SERVERNAME /V /FO csv|find /i „domain“
Dabei steht „Domain“ in beiden Beispielen für den Namen der eigenen Domäne, den man hier ersetzt. Die Ausgabe dieses Batches leitet man in eine Datei um. Die Kommandos verbinden sich mit jedem Remoteserver und geben über das Piping mit find nur die Tasks zurück, in denen der Name der Domäne auftaucht – üblicherweise dann, wenn ein Domänenkonto für den Task angegeben ist. In der erzeugten Ausgabedatei findet man also nun genau die Tasks, die man nach der Kennwortänderung bearbeiten muss.
Skript-Download
Hier die Skripte für Teil 1 als Download.
Service-Inventar (2,8 KiB, 1.708-mal heruntergeladen, letzte Änderung am 19. April 2012)
http://faq-o-matic.net/?p=1014