Montag, 21. Juli 2014

Mehrstufige Eskalation von Incidents

Der produktive Einsatz des Service Managers schreitet vorran und es fällt auf, das mache Incidents leider nach geraumer Zeit noch nicht bearbeitet wurden.



Um dem vorzubeugen wollten wir eine Eskalation der Tickets einbauen, die unsere Mitarbeiter über das unbearbeitete Ticket informiert. Da der Service Manager leider über keine Eskalation in unserem Sinne verfügt, strickten wir uns eine mit Hilfe der SMLets.



Zunächst erstellen wir eine Funktion zum Auslesen der Mailvorlagen, die im Falle einer Eskalation an den PrimaryOwner versandt werden.In unserem Fall handelt es sich bei den PrimaryOwner um eine AD-Gruppe, in der alle Mitglieder der SupportGroup enthalten sind.

function get-MailContent ($templatename, $IR)
{
[String]$MailContent = [System.IO.File]::ReadAllText( "C:\Skripts\Mails\" + $templatename)
[string]$MailContent = $MailContent.Replace("#ID", $IR.Id)
[string]$MailContent = $MailContent.Replace("#TITLE", $IR.Title)
return $MailContent | Out-String;
echo "--get-MailContent geladen"
}



Als nächstes importieren wir die SMLets und binden die benötigten Klassen ein.

Import-Module smlets;

#benötigte Klassen
$classAnLog = Get-SCSMClass System.WorkItem.TroubleTicket.AnalystCommentLog;
$ClassIncident = Get-SCSMClass System.WorkItem.Incident$;
$AssignedToUserClass = Get-SCSMRelationshipClass System.WorkItemAssignedToUser;
$PrimaryOwnerClass = Get-SCSMRelationshipClass System.WorkItem.IncidentPrimaryOwner$



Hier muss die ID gewechselt werden. Es werden aussließlich die Incidents eskalaiert, die sich in diesen Status befinden.

#StatusID´s der Status New und InfoRecived
$IDNew = (Get-SCSMEnumeration -Name Enum.cfbcf3e99aaa4609a876f29bb138957a).id;
$IDInfoRecived = (Get-SCSMEnumeration -Name Enum.957065f4277a484794833985748bdc8a).id;


Wir laden uns die Incidents, die sich im Status "New" oder "InfoRecived" befinden.

    #Incidents im Status New und InfoRecived auslesen
    $IncidentswhichCouldEscalate = Get-SCSMObject -Class $ClassIncident -Filter "Status -eq $IDNew";
    $IncidentswhichCouldEscalate = $IncidentswhichCouldEscalate + (Get-SCSMObject -Class $ClassIncident -Filter "Status -eq $IDInfoRecived");




Nun arbeiten wir alle zuvor gefiltererten Incidents ab.

ForEach($incident in $IncidentswhichCouldEscalate)
    {
        #AssignedUser auslesen
        $User = Get-SCSMRelatedObject -SMObject $incident -Relationship $AssignedToUserClass;

        #Nur wenn kein User zugewiesen ist
        if(!$user)
        {
            #Nur, wenn der Incident noch nicht eskaliert ist
            if($incident.Escalated -eq $false)
            {               

                #Der Incident wurde innerhalb der letzten 4 Stunden nicht bearbeitet
                if($incident.LastModified -lt (Get-Date).AddHours(-4))
                {
                    #Eskalation auf true setzen und Eskalation I im Actionlog eintragen
                    $incident | Set-SCSMObject -Property Escalated -Value true;
                    $incident | Set-SCSMIncident -Comment "Eskalation I";
               
                    #PrimaryOwner raussuchen
                    $PrOwner = Get-SCSMRelatedObject -SMObject $incident -Relationship $PrimaryOwnerClass
               
                    [String]$Content = get-MailContent("EskalationI.txt") ($incident)
                   
                    #Mail an PrimaryOwnerGroup

                    Send-MailMessage -SmtpServer "smtp.domain.de" -To ($PrOwner.UserName + "@domain.de") -From "Helpdesk " -Subject ("Eskalation " + $incident.Id) -Body $Content -Encoding ([System.Text.Encoding]::UTF8) -BodyAsHtml

                }
            }
            #Nur, wenn der Incident bereits eskaliert ist
            elseif($incident.Escalated -eq $true)
            {
               
                #Für jedes Comment das zum Incident gehört durchführen

                #Objekt der Klasse SCSMIncident des zZ zu bearbeitenden Incidents erstellen
                $incdentDefInfos = Get-SCSMIncident -ID $incident.Id

                #Bearbeiterkommentare des Incidents auslesen und nach Eintragungsdatum sortieren
                $CommentList = $incdentDefInfos.AppliesToTroubleTicket | where {$_.Classname -eq "System.WorkItem.TroubleTicket.AnalystCommentLog" } | sort -Property EnteredDate
           
                #Kommentarliste von hinten durcharbeiten
                for($CountComments = $CommentList.Length; $CountComments -ge 0; $CountComments--)
                {  
                    #Kommentar von ServiceAccount erstellt
                    if($CommentList.EnteredBy[$CountComments] -eq 'domain\ServiceAccount')
                    {

                        #Ist das zuletzt erstellte Kommentar von ServiceAccount "Eskalation I", so darf der Incident ein zweites mal eskalieren
                        if($CommentList.Comment[$CountComments] -eq 'Eskalation I')
                        {   

                            #Eskalation2 //Wenn Eskal I zutrifft und seither 4h vergangen sind                       
                            if($CommentList.EnteredDate[$CountComments] -lt (Get-Date).AddHours(-4))
                            {                   
                                #Eskalation auf true setzen und Eskalation I im Actionlog eintragen
                                $incident | Set-SCSMObject -Property Eskalated -Value true
                                $incident | Set-SCSMIncident -Comment "Eskalation II" 
                           
                              
                                #PrimaryOwner raussuchen
                                $PrOwner = Get-SCSMRelatedObject -SMObject $incident -Relationship $PrimaryOwnerClass

                                [String]$Content = get-MailContent("EskalationII.txt") ($incident)

                                #Mail an PrimaryOwner Group

                                Send-MailMessage -SmtpServer "smtp.domain.de" -To ($PrOwner.UserName + "@domain.de") -From "Helpdesk " -Subject ("Eskalation Stufe 2  " + $incident.Id) -Body $Content -Encoding ([System.Text.Encoding]::UTF8) -BodyAsHtml

                            }
                            break;                       
                        }
                        #Kommt allerigns zuerst ein Kommentar mit "Eskalation II", so ist der Incident bereits 2* eskaliert und es muss abgebrochen werden
                        elseif($CommentList.Comment[$CountComments] -eq 'Eskalation II')
                        {
                            break;
                        }
                    }
                }
            }
        }
    }

Montag, 10. Februar 2014

WMI via PHP

Nach dem ich über einen interessanten Artikel von Taylor Ren [Hier] gestolpert bin, habe ich mich entschieden dort mal etwas tiefer zu graben. Ich bin schon häufiger mit PHP-Webservices konfrontiert worden und wollte dort gewisse Informationen oder Funktionen via WMI abfragen. Leider war ich immer wieder dazu gezwungen Workarounds anzuwenden. Nach diesem Artikel bin ich der Sache auf den Grund gegangen. Heraus gekommen ist eine Klasse in PHP die WMI auf Windows-Servern erleichtern soll. Viel Spaß damit.

wmi.class.php

Dienstag, 24. September 2013

Scripting: Get Loggedon User + Informations

Ab und an kann es sein, dass man den aktuell am System angemeldete Benutzer herausfinden möchte. Relativ einfach ist es mit PowerShell und Get-WmiObject:

 Get-WmiObject -Class Win32_ComputerSystem | SELECT Username

Als Ergebnis erhält man:

PS C:\>  Get-WmiObject -Class Win32_ComputerSystem | SELECT Username

Username
--------
Domain\schilzm


Das der aktuelle Benutzer an meinem Rechner schilzm ist, wer hätte es gedacht!? Interessant wird es dann wenn man via Get-WmiObject auf einen Remotecomputer zugreift. Dies geht recht einfach über den Parameter Computer:

Get-WmiObject -Class Win32_ComputerSystem -Computer RemotePC1 | SELECT Username


Das Ergebnis ist analog zum vorherigen. Der Remotecomputer muss natürlich richtig konfiguriert sein. Eine gute Übersicht bietet folgender Artikel:

WindowsPro: Remote-Zugriffe für WMI konfigurieren und testen

Wenn man jetzt etwas mehr Informationen über den Benutzer haben möchte, kann man das mit der WMI-Klasse Win32_NetworkloginProfile erreichen.

Als einen ersten Test gibt man folgendes ein:
Get-WmiObject -Class Win32_NetworkloginProfile

Ergebnis:
PS C:\> Get-WmiObject -Class Win32_NetworkloginProfile


Caption      : NT-AUTORITÄT\SYSTEM
Privileges   :
Profile      :
UserId       :
UserType     :
Workstations :

Caption      : schilzm
Privileges   : 1
Profile      : \\path\to\schilzm
UserId       : 23926
UserType     : Normal Account
Workstations :

Wie man sieht gibt es zwei Instanzen der Klasse. Damit man nun die des interessanten Benutzers bekommt macht man ein Query:
Get-WmiObject -Query "SELECT * FROM Win32_NetworkLoginProfile WHERE Caption = 'schilzm'"

Ergebins:
PS C:\> Get-WmiObject -Query "SELECT * FROM Win32_NetworkLoginProfile WHERE Caption = 'schilzm'"


Caption      : schilzm
Privileges   : 1
Profile      : \\path\to\schilzm
UserId       : 23926
UserType     : Normal Account
Workstations :

Aber welche Informationen bekommt man nun aus der Class Win32_NetworkLoginProfile!? Um das zu sehen verwendet man das Cmdlet "Get-Member":
Get-WmiObject -Class Win32_NetworkLoginProfile | Get-Member

Der Ergebnis:

Name                MemberType   Definition
----                ----------   ----------
AccountExpires      Property     System.String AccountExpires {get;set;}
AuthorizationFlags  Property     System.UInt32 AuthorizationFlags {get;set;}
BadPasswordCount    Property     System.UInt32 BadPasswordCount {get;set;}
Caption             Property     System.String Caption {get;set;}
CodePage            Property     System.UInt32 CodePage {get;set;}
Comment             Property     System.String Comment {get;set;}
CountryCode         Property     System.UInt32 CountryCode {get;set;}
Description         Property     System.String Description {get;set;}
Flags               Property     System.UInt32 Flags {get;set;}
FullName            Property     System.String FullName {get;set;}
HomeDirectory       Property     System.String HomeDirectory {get;set;}
HomeDirectoryDrive  Property     System.String HomeDirectoryDrive {get;set;}
LastLogoff          Property     System.String LastLogoff {get;set;}
LastLogon           Property     System.String LastLogon {get;set;}
LogonHours          Property     System.String LogonHours {get;set;}
LogonServer         Property     System.String LogonServer {get;set;}
MaximumStorage      Property     System.UInt64 MaximumStorage {get;set;}
Name                Property     System.String Name {get;set;}
NumberOfLogons      Property     System.UInt32 NumberOfLogons {get;set;}
Parameters          Property     System.String Parameters {get;set;}
PasswordAge         Property     System.String PasswordAge {get;set;}
PasswordExpires     Property     System.String PasswordExpires {get;set;}
PrimaryGroupId      Property     System.UInt32 PrimaryGroupId {get;set;}
Privileges          Property     System.UInt32 Privileges {get;set;}
Profile             Property     System.String Profile {get;set;}
ScriptPath          Property     System.String ScriptPath {get;set;}
SettingID           Property     System.String SettingID {get;set;}
UnitsPerWeek        Property     System.UInt32 UnitsPerWeek {get;set;}
UserComment         Property     System.String UserComment {get;set;}
UserId              Property     System.UInt32 UserId {get;set;}
UserType            Property     System.String UserType {get;set;}
Workstations        Property     System.String Workstations {get;set;}
ConvertFromDateTime ScriptMethod System.Object ConvertFromDateTime();
ConvertToDateTime   ScriptMethod System.Object ConvertToDateTime();

Wie man sieht gibt es hier einige Informationen zu dem Benutzer. Mutmaßlich sind nicht alle relevant für die zu erfüllende Aufgabe. Mit folgendem Code kann man sich aus den beiden Klassen die Informationen zusammen stellen die man möchte und dann ein neues Object zurückgeben, dass alle Informationen enthält:
[WMI]$cs  = Get-WmiObject -Class Win32_Computersystem
[WMI]$nlp = Get-WmiObject -Query "SELECT * FROM Win32_NetworkLoginProfile WHERE Caption = '$($cs.username.split("\")[1])'"

 [hashtable]$UserInfo = @{
    'LoggedOnComputer'=$cs.Name;
    'Model' = $cs.Model;
    'Manufacturer' = $cs.Manufacturer;
    'Username' = $nlp.Caption;
    'Fullname' = $nlp.FullName;
    'LastLogon' = $nlp.ConverttoDateTime($nlp.LastLogon);
    'LogonTime' = New-Timespan -start $nlp.ConverttoDateTime($nlp.LastLogon) -End (Get-Date)
 }
$UserInfo

Das Ergebnis:
Name                           Value
----                           -----
LogonTime                      19:47:05.9934687
Manufacturer                   Dell Inc.
LoggedOnComputer               MyCompi1
LastLogon                      23.09.2013 16:23:51
Username                       schilzm
Model                          Precision T1500
Fullname                       Schilz, Marc

Installation Visual Studio 2012

Hier finden Sie eine kurze und einfache Anleitung zur Installation von Visual Studio 2012.

Visual Studio wird unter anderem für die Installation des Workflow Manager 1.0 benötigt.


Zunächst muss sichergestellt sein, dass der angemeldete Benutzer, der die Installation durchführen soll, ein eingetragener Administrator der Maschine ist.
Zudem sollte geprüft werden, ob bereits eine weitere Version von Visual Studio installiert ist. Weitere Informationen dazu finden Sie hier: Parallele Installation mehrerer Versionen

Als nächstes wird das Setup von Visual Studio 2012 ausgeführt und im ersten Schritt der Pfad für die Installation angegeben(Achtung! Die Installation benötigt ca. 8.5GB Speicher.).

Standartpfad: C:\Program Files(x86)\Microsoft Visual Studio 11.0

Im nächsten Schritt werden die benötigten Features ausgewählt.
Abb. 1: MS Visual Studio Features
 Durch betätigen der "Install"-Schaltfläche beginnt die Installation von Visual Studio.

Abb. 2: Installation Successful
Wurde die Installation wie in Abb. 2 erfolgreich ausgeführt, muss ein Neustart des Systems erfolgen.

Im Anschluss starten Sie Visual Studio und wählen im folgenden Fenster Ihre gewünschten Standardsettings aus und bestätigen Ihre Aauswahl mit "Start Visual Studio"

Abb. 3: Choose Default Settings

Jetzt sollte sich Visual Studio 2012 öffnen und der Spaß kann beginnen.



Windows Server 2008R2: Snipping Tool

Bei Windows Server 2008R2 ist das allseits beliebte Snipping Tool leider nicht von Hause aus implementiert. Es kann allerdings durch folgende Schritte schnell und einfach eingefügt werden.



Dazu wird im Server Manager ein neues Feature hinzugefügt:

Start\Administrative Tools\Server Manager(Server Verwaltung)\Features
Server Manager\Features


Durch klicken auf "Add Features" (Features hinzufügen) wird folgendes Fenster geöffnet:

Add Features Wizard
Hier muss nun das Feature "Desktop Experience" (Desktopdarstellung) ausgewählt werden. Durch das Setzten des Hakens öffnet sich für gewöhnlich ein weiteres Fenster, welches die Installation weiterer benötigter Features abfragt.

Add required Features

Mit Bestätigung des Fensters beginnt die Installation des Features.

Nach der Fertigstellung dieses Vorgangs ist ein Neustart des Systems von nöten.


Ab sofort findet man das zuvor vermisste Snipping Tool unter Start\All Programs\Accessories\SnippingTool.exe