Logo faq-o-matic.net
Logo faq-o-matic.net

Dienst- und Task-Konten identifizieren

von veröffentlicht am25. Dezember 2008, 10:00 Uhr Kurzlink http://faq-o-matic.net/?p=1014 Zitatlink
Kategorie Kategorie: Administration, Scripting, Sicherheit, Windows   Translate with Microsoft Translator Translate EN
Zuletzt aktualisiert: 13. November 2011

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. 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.

 

  1. ' """"""""""""""""""""""""""
  2. ' ServiceInv.vbs
  3. ' Dienst-Mini-Inventarisierung über WMI
  4. '
  5. ' Version: 0.1
  6. ' Datum:   22.12.2005
  7. ' Autor:   Nils Kaczenski (Vorname at Nachname .de)
  8. ' Letzte Änderungen:
  9. '
  10. ' Nils Kaczenski stellt dieses Skript ohne jede
  11. ' Gewährleistung zur Verfügung.
  12. ' Die Verwendung geschieht auf eigene Gefahr.
  13. '
  14. ' """"""""""""""""""""""""""
  15. Option Explicit
  16. Dim colItems               '
  17. Dim dtmCompInDB            '
  18. Dim intGesamtzahl          '
  19. Dim intZeilenzahl          '
  20. Dim objArgs      '
  21. Dim objConn                '
  22. Dim objItem
  23. Dim objRS                  '
  24. Dim objWMIService
  25. Dim objWSHNetwork          '
  26. Dim strBenutzer            '
  27. Dim strCaption             '
  28. Dim strComputer            '
  29. Dim strConn                '
  30. Dim strDatenbank           '
  31. Dim strDBServer            '
  32. Dim strLogmode             '
  33. Dim strName                '
  34. Dim strPathName            '
  35. Dim strSQL                 '
  36. Dim strStartMode           '
  37. Dim strStartName           '
  38. Dim strState               '
  39. strLogmode = "error"
  40. say Now & ": Starte Dienst-Inventarisierung …"
  41. On Error Resume Next
  42. ' Computernamen herausfinden
  43. Set objWSHNetwork = CreateObject("Wscript.Network")
  44. strComputer = objWSHNetwork.ComputerName
  45. strBenutzer = objWSHNetwork.UserDomain & "\" & objWSHNetwork.UserName
  46. If checkit("Fehler beim Zugriff auf WSHNetwork!") Then WScript.Quit
  47. ' falls Computername per Argument übergeben, wird dieser genommen:
  48. Set objArgs = WScript.Arguments
  49. If objArgs.Count <> 0 Then
  50.     strComputer = objArgs(0)
  51. End If
  52. saydebug strComputer
  53. ' Datenbank-Daten
  54. strDBServer = "SQL01"
  55. strDatenbank = "MeineAdminDB"
  56. ' Objekte
  57. Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
  58. If checkit("Fehler beim Zugriff auf WMI!") Then WScript.Quit
  59. Set objConn = CreateObject("ADODB.Connection")
  60. Set objRS = CreateObject("ADODB.Recordset")
  61. If checkit("Fehler beim ADODB-Zugriff!") Then WScript.Quit
  62. ' Datenbank-Verbindung
  63. strConn = "Provider=SQLOLEDB;Integrated Security=SSPI;" _
  64.     & "Initial Catalog=" & strDatenbank _
  65.     & ";Data Source=" & strDBServer
  66. objConn.Open strConn
  67. If checkit("Fehler beim Verbinden mit der Datenbank!") Then WScript.Quit
  68. ' prüfen, ob aktuelle Daten da sind
  69. strSQL = "select max(RowDate) as datum from dbo.Win32Service where " _
  70. & "SystemName='" & strComputer & "'"
  71. objRS.Open strSQL, objConn
  72. If checkit("Fehler beim Abfragen der Datenbank!") Then WScript.Quit
  73. dtmCompInDB = objRS("Datum")
  74. If DateDiff("d", dtmCompInDB, Now) < 7 Then
  75.     ' Daten sind aktuell – Skript verlassen
  76.     say "Vorhandene Daten sind aktuell."
  77.     say "Beende Service-Inventarisierung."
  78.     WScript.Quit
  79. End If
  80. objRS.Close
  81. ' vorhandene Daten dieses Rechners löschen
  82. strSQL = "delete from Win32Service where SystemName='" & strComputer & "'"
  83. objConn.Execute strSQL, intZeilenzahl
  84. If checkit("Fehler beim Löschen der Inventardaten!") Then WScript.Quit
  85. say intZeilenzahl & " Zeilen aus der Datenbank gelöscht."
  86. ' WMI-Daten auslesen
  87. Set colItems = objWMIService.ExecQuery("Select * from Win32_Service",,48)
  88. If checkit("Fehler beim Auslesen der WMI-Daten!") Then WScript.Quit
  89. ' WMI-Daten durchlaufen
  90. For Each objItem In colItems
  91.     strCaption = objItem.Caption
  92.     strName =  objItem.Name
  93.     strPathName =  objItem.PathName
  94.     strStartMode =  objItem.StartMode
  95.     strStartName =  objItem.StartName
  96.     strState =  objItem.State
  97.     If checkit("Fehler beim Durchlaufen der WMI-Daten, Item " & intGesamtzahl & "!") Then WScript.Quit
  98.     ' SQL-Kommando aufbauen
  99.     strSQL = "insert dbo.Win32Service (" _
  100.     & " SystemName, Caption, [Name], PathName, StartMode," _
  101.     & " StartName, State, RowDate, Username " _
  102.     & ") values ( '" _
  103.     & strComputer & "', '" & strCaption & "', '" & strName _
  104.     & "', '" & strPathName & "', '" & strStartMode _
  105.     & "', '" & strStartName & "', '" & strState & "', '" _
  106.     & Now & "', '" & strBenutzer & "') "
  107.     saydebug strSQL
  108.     ' Daten in die Datenbank schreiben
  109.     objConn.Execute strSQL, intZeilenzahl
  110.     If checkit("Fehler beim Schreiben in die Datenbank!") Then WScript.Quit
  111.     intGesamtzahl = intGesamtzahl + intZeilenzahl
  112. Next
  113. objConn.Close
  114. On Error Goto 0
  115. say intGesamtzahl & " Zeilen in die Datenbank geschrieben."
  116. say "Service-Inventarisierung ist fertig."
  117. Function checkit(strNachricht)
  118.     checkit = False
  119.     If Err.number <>0 Then
  120.         strNachricht = strNachricht & " [" & Err.description & " (" & Err.number & ")]"
  121.             sayerror strNachricht
  122.         Err.clear
  123.         checkit=True
  124.     End If
  125. End Function
  126. ' Universelle Ausgabefunktion
  127. Sub say(s)
  128.     saynb s & VbCrLf
  129. End Sub
  130. ' Ausgabe ohne Zeilenumbruch
  131. Sub saynb(s)
  132.     WScript.echo s
  133. End Sub
  134. ' Ausgabe als Fehlermeldung
  135. Sub sayerror(s)
  136.     say "Fehler (" & Date & ", " & Time & "): " & s
  137. End Sub
  138. ' Ausgabe als Warnung
  139. Sub sayalert(s)
  140.     say "Warnung (" & Date & ", " & Time & "): " & s
  141. End Sub
  142. ' Debug-Ausgabe
  143. Sub saydebug(s)
  144.     If strLogmode = "debug" Then
  145.         say s
  146.     End If
  147. 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:

  1. SELECT StartName
  2. , Caption
  3. , SystemName
  4. , StartMode
  5. FROM Win32Service
  6. WHERE StartName NOT IN (
  7. 'LocalSystem'
  8. , 'NT AUTHORITY\LocalService'
  9. , 'NT AUTHORITY\NetworkService')
  10. 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"

Dabei steht "Domain" 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.

Verwandte Beiträge:

  1. Systeme mit Windows Server 2003 R2 identifizieren
    Es ist nicht ganz leicht, im Rahmen einer Dokumentation im Netzwerk die Server zu identifizieren, die mit Windows Server 2003...
  2. Ein AD-Attribut zu einem Logon-Namen herausfinden
    Das folgende Skript gibt den Wert eines beliebigen Attributs aus dem Active Directory für einen User zurück, dessen Logon-Name (SAM-Name,...
  3. Standardkennwort bei Benutzern prüfen
    Manchmal werden Benutzer durch eine Migration, beim Neuanlegen oder auch beim Zurücksetzen des Kennwortes mit einem Standardkennwort versehen. Ab und...
  4. AD-Informationen schnell auslesen
    Neben eineAm klassischen ADSI-Skript gibt es eine relativ einfache Möglichkeit, Informationen aus dem Active Directory auszulesen. Hierzu lässt sich das...
  5. Ärger mit Anführungszeichen
    Es kommt manchmal vor, dass man mit VB Script einen recht einfachen Befehl ausführen will, z.B. diesen hier: "c:\Program Files\attrib.exe"...

© 2005-2012 bei faq-o-matic.net. Alle Rechte an den Texten liegen bei deren Autorinnen und Autoren.

Jede Wiederveröffentlichung der Texte oder von Auszügen daraus - egal ob kommerziell oder nicht - bedarf der ausdrücklichen Genehmigung durch die jeweiligen Urheberinnen oder Urheber.

Das Impressum findet sich unter: http://www.faq-o-matic.net/impressum/

Danke, dass du faq-o-matic.net nutzt. Du hast ein einfaches Blog sehr glücklich gemacht!