Tag-Archiv für » community «

06 | 09 | 2018

CIM Lingen 2018 – Folien und Code-Snippets zu meinem Vortrag

Geschrieben von um 16:35 Uhr

Am 01.09.2018 durfte ich auf der diesjährigen CIM Lingen einen Vortrag zum Thema „Running Scripts in the Enterprise“ halten. Ein Thema freilich für strategische Beratungen und lange Refactoring-Projekte, das ich versucht habe, in 45 Minuten zu pressen.

Dennoch konnte ich es mir nicht nehmen lassen, zwei Live Demos einzubauen, und da diese nicht in den Folien waren, poste ich die Code Snippets für Windows und Linux ebenfalls hier. Enjoy!

Demo 1: Konfiguration zu Beginn des Skriptes lesen

Sowohl in der Test- als auch in der Produktionsumgebung ist ein Webserver vorhanden, der auf den Namen https://simpleconfig.it-pro-berlin.de hört (da DNS in Test und in Prod aber voneinander unabhängig ist, löst sich dieser Name zu unterschiedlichen Maschinen auf). Dieser Server stellt die Datei config.xml mit dem folgenden Inhalt zur Verfügung:

<scriptconfig>
	<runglobal>true</runglobal>
	<sql dbserver="ciminfra.cim.it-pro-berlin.de" dbname="ScriptsDB" dbuser="confread" dbpasswd="confread">Production SQL Database</sql>
</scriptconfig>

Die Aufgabe lautet, den Wert von „runglobal“ auszuwerten und, falls er auf „true“ steht, mit den in der nächsten Zeile enthaltenen Verbindungsdaten zu einem SQL Server zu verbinden und aus der Tabelle „SETTINGS“ ddie Einstellung „ENVIRONMENT“ auszulesen. Die Tabelle enthält zwei Spalten: „settingname“ und „settingvalue“.

Hier ist die Lösung in PowerShell. Dieser Code funktioniert unverändert in Windows PS, PSCore auf Windows und PSCore auf Linux.

function Get-CIMConfig {
    $config_url = "https://simpleconfig.it-pro-berlin.de/config.xml"
    $res = $null
    try {
        $http_response = Invoke-WebRequest -Uri $config_url -TimeoutSec 10
        if ($http_response.StatusCode -eq 200) {
            $config = ($http_response.content)
            if ($config.scriptconfig.runglobal -eq "true") {
                $res = New-Object PSObject -Property @{
                    "dbserver" = $config.scriptconfig.sql.dbserver
                    "dbname" = $config.scriptconfig.sql.dbname
                    "dbuser" = $config.scriptconfig.sql.dbuser
                    "dbpasswd" = $config.scriptconfig.sql.dbpasswd
                }
            }
        }
    } catch {}
    $res
}

#Jedes Skript beginnt mit
$cc = Get-CIMConfig
if ($cc) {
    Write-Host "Ausführung gestattet..."
    $dbconnstr = "Server=$($cc.dbserver);Database=$($cc.dbname);uid=$($cc.dbuser);pwd=$($cc.dbpasswd)"
    $dbconn = New-Object System.Data.SqlClient.SqlConnection
    $dbconn.ConnectionString = $dbconnstr
    $dbconn.Open() | Out-Null
    $dbcmd = New-Object System.Data.SqlClient.SqlCommand
    $dbcmd.Connection = $dbconn
    $dbcmd.CommandText = "SELECT settingvalue FROM SETTINGS WHERE settingname='ENVIRONMENT'"
    $dbres = $dbcmd.ExecuteReader()
    $dbtable = New-Object System.Data.DataTable
    $dbtable.Load($dbres)
    Write-Host ("Aktuelle Umgebung: {0}" -f $dbtable.Rows[0]['settingvalue'])
}

In Produktion würde man natürlich noch ein wenig mehr Überprüfung und Logging einbauen, aber das ist der wesentliche Teil. Dieser Code benötigt keine externen Module oder Bibliotheken.
In VBS ist der Code etwas sperriger, macht aber was er soll und liefert das gewünschte Ergebnis:

strConfigURL = "https://simpleconfig.it-pro-berlin.de/config.xml"
Set wshShell = CreateObject("WScript.Shell")
strConfigLocal = wshShell.ExpandEnvironmentStrings("%TEMP%") + "\config.xml"
wshShell.Run("cmd.exe /C del /q " + strConfigLocal)

Set objHTTP = CreateObject("WinHttp.WinHttpRequest.5.1")
objHTTP.Open "GET", strConfigURL, False
objHTTP.Send
If objHTTP.Status = 200 Then
    Dim objStream
    Set objStream = CreateObject("ADODB.Stream")
    With objStream
        .Type = 1 'adTypeBinary
        .Open
        .Write objHTTP.ResponseBody
        .SaveToFile strConfigLocal
        .Close
    End With
    Set objStream = Nothing
End If

Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.Async = "False"
xmlDoc.Load(strConfigLocal)
Set colNodes = xmlDoc.SelectNodes("//scriptconfig/runglobal")
For Each objNode in colNodes
    runglobal = objNode.Text
Next
If runglobal = "true" Then
    Set colNodes = xmlDoc.SelectNodes("//scriptconfig/sql")
    For Each objNode in colNodes
        dbserver = objNode.Attributes.GetNamedItem("dbserver").Text
        dbname = objNode.Attributes.GetNamedItem("dbname").Text
        dbuser = objNode.Attributes.GetNamedItem("dbuser").Text
        dbpasswd = objNode.Attributes.GetNamedItem("dbpasswd").Text
    Next
    strDBConnStr = "Provider=SQLOLEDB;Data Source=" & dbserver & ";Initial Catalog=" & dbname & ";User ID=" & dbuser & ";Password=" & dbpasswd
End If
'WScript.Echo(strDBConnStr)
Set objDBConn = CreateObject("ADODB.Connection")
objDBConn.Open(strDBConnStr)
Set objRS = CreateObject("ADODB.Recordset")
objRS.Open "SELECT settingvalue FROM SETTINGS WHERE settingname='ENVIRONMENT'", objDBConn, 3, 3
objRS.MoveFirst
WScript.Echo("Aktuelle Umgebung: " & objRS.Fields.Item("settingvalue"))
objRS.Close()
objDBConn.Close()

Die Shell auf Linux (geht in POSIX und in BASH) braucht zwei externe Pakete, XMLLINT und FreeTDS, danach funktioniert es mit

#!/bin/sh
wget -q "https://simpleconfig.it-pro-berlin.de/config.xml" -O ~/config.xml
runglobal=$(xmllint --xpath "//scriptconfig/runglobal/text()" ~/config.xml)
if [ $runglobal = true ]
    then
        echo "Ausführung gestattet..."
        dbserver=$(xmllint --xpath "string(//scriptconfig/sql/@dbserver)" ~/config.xml)
        dbname=$(xmllint --xpath "string(//scriptconfig/sql/@dbname)" ~/config.xml)
        dbuser=$(xmllint --xpath "string(//scriptconfig/sql/@dbuser)" ~/config.xml)
        dbpasswd=$(xmllint --xpath "string(//scriptconfig/sql/@dbpasswd)" ~/config.xml)
        env=$(bsqldb -U $dbuser -P $dbpasswd -S $dbserver -D $dbname -i sico1.sql 2>&-)
        echo "Umgebung: " $env
fi

Die Datei sico1.sql könnte man natürlich dynamisch aus dem Skript generieren, für den Zweck der Demo ist sie aber statisch und hat den folgenden wenig überraschenden Inhalt:

SELECT settingvalue FROM SETTINGS WHERE settingname='ENVIRONMENT'

An der CMD-Shell schaut es natürlich etwas mau aus, dort hat man sich in der Vergangenheit ja immer wieder mit einer selbstgeschriebenen EXE beholfen. Man kann natürlich auch das o.g. VBS-Skript zu Begimn des CMD-Batchscripts bemühen.

Demo 2: Auf einen SYSLOG-Server schreiben

Einträge auf einem SYSLOG-Server erzeugen, das schaffen Skriptsprachen auch. PowerShell (auch Core) nutzt die System.Net-Klassen:

$syslog_server = 'ciminfra.cim.it-pro-berlin.de'
$Log_Message = 'PowerShell:TEST'
#0=EMERG 1=Alert 2=CRIT 3=ERR 4=WARNING 5=NOTICE  6=INFO  7=DEBUG
$Log_Severity = '1'
#(16-23)=LOCAL0-LOCAL7
$Log_Facility = '22'
# Calculate the priority
$Log_Prio = ([int]$Log_Facility * 8) + [int]$Log_Severity
$Sender = $env:COMPUTERNAME
#Time format the SW syslog understands
$TS = Get-Date -Format "MMM dd HH:mm:ss"
# Assemble the full syslog formatted message
$FullSyslogMessage = "<{0}>{1} {2} {3}" -f $Log_Prio, $TS, $Sender, $Log_Message
$FullSyslogMessage
# create an ASCII Encoding object
$Encoding = [System.Text.Encoding]::ASCII
# Convert into byte array representation
$ByteSyslogMessage = $Encoding.GetBytes($FullSyslogMessage)

$UDPCLient = New-Object System.Net.Sockets.UdpClient
$UDPCLient.Connect($syslog_server, 514)
$UDPCLient.Send($ByteSyslogMessage, $ByteSyslogMessage.Length)
$UDPCLient.Close()

VBS und CMD-Shell müssen hier, wenig überraschend, ohne externe Unterstützung passen, die BASH-Shell hingegen ist der absolute Champion (das gilt aber nicht für die POSIX-Shell):

#!/bin/bash
msg="Linux_bash:TESTLOG Definitive"
facility=21 #local5
severity=4 #WARN
prio=$((8 * $facility + $severity))
syslogserver="ciminfra.cim.it-pro-berlin.de"
hname=$(hostname)
ts=$(date "+%b %d %H:%M:%S")
syslogmsg="<$prio>$ts $hname $msg"
echo "$syslogmsg" > /dev/udp/$syslogserver/514

Folien von der CIM

Und hier die Folien zum Download:

Tags » , , , , , «

+

02 | 09 | 2018

CIM Lingen 2018 – das war schön

Geschrieben von um 8:17 Uhr

Gestern hatte ich die Ehre, auf der CIM Lingen 2018 zu „Running Scripts in the Enterprise“ (was sonst?) zu sprechen. Bis dato hatte ich die CIM gar nicht auf dem Schirm gehabt, aber die Emsländer Community-Konferenz hat sich als eine ganz tolle Veranstaltung entpuppt. Trotz einer ordentlichen Größe von 300 Teilnehmern ist die Atmosphäre eines User Group-Treffens unverkennbar vorhanden. An dieser Stelle nochmals vielen Dank an das Orga-Team und natürlich an die Sponsoren, zu denen sowohl mein jetziger als auch mein ehemaliger Arbeitgeber gehören.

Ich habe einige tolle Menschen kennengelernt und einige bisher nur aus dem Online-Geschehen bekannte endlich in Person getroffen. Etwas traurig war es zu sehen, dass langjährigen MVPs und absoluten Grundsteinen der deutschen Microsoft-Community die Auszeichnung mit der Begründung „too little cloud content“ entzogen wurde. Schämt euch, Microsoft! Ihr werdet schon sehen, was ihr davon habt.

Nächstes Jahr soll die (nunmehr 15.) CIM an zwei Tagen stattfinden – 13. + 14.09.2019 (save the date!) und über acht Tracks gehen. Es sind 50 Sessions angekündigt, es ergeben sich also rechnerisch, abzüglich der Opening und der Closing Keynote, drei Slots pro Tag in jedem Track. Somit hätte jeder Slot voraussichtlich eine Länge, die einen deutlich tieferen Einstieg in das jeweilige Thema erlaubt. Drückt mir die Daumen – das wäre genau mein Ding, denn diesmal bin ich etwas aus der Zeit geraten und musste am Ende des 45-minütigen Slots ganz schön hetzen.

Also: auf zur nächsten CIM: #cimlingen #communityrocks

 

Tags » , , , «

+

09 | 01 | 2018

Die Windows Server User Group Berlin trifft sich am 25.01.2018

Geschrieben von um 20:30 Uhr

Als Host und Sponsor beherbergt uns diesmal die Jaemacom GmbH.

Agenda und Anmeldung: [ KLICK ]

Tags » , , , «

+

16 | 12 | 2016

Erstes Treffen der EXUSG Berlin – das war schön :-)

Geschrieben von um 19:25 Uhr

Gestern haben wir uns bei der dama.go am Spittelmarkt zum ersten Mal getroffen. Der Abend war ein voller erfolg – geballtes Know-How in einem Raum, inspirierende Vorträge, tolle Gespräche und auch ein bißchen Nostalgie 🙂

Mein Vorschlag, Termine für die Treffen in 2017 bereits jetzt festzulegen, wurde zumindest nicht abgelehnt. Die Termine stehen unter http://exusg.de/veranstaltungen/terminplanung-2017/ zur Abstimmung nach dem Motto „wer was dagegen hat, möge sich jetzt äußern oder für immer schweigen“.

Ich freue mich schon auf das nächste Treffen, voraussichtlich im Februar.

Tags » , , , , , , «

+

10 | 10 | 2016

PowerShell Hack: TechNet-Teilnahme eines Users messen oder eine Übung im Web Grabbing

Geschrieben von um 9:04 Uhr

Wenn man im TechNet-Forum (https://social.technet.microsoft.com/Forums/de-de/home) unterwegs ist, kann man ja Punkte verdienen. Hat man eine Frage beantwortet und hat der Fragesteller das bestätigt, gibt es Punkte. Hat jemand den Post für hilfreich erklärt, gibt’s auch Punkte. Ist man in vielen Microsoft-Technologien unterwegs, möchte man manchmal wissen, wo man denn die meiste Contribution geleistet hat. Das Forumsystem selbst gibt da zwar eine sehr detaillierte Auskunft in Form einer Aktivitäten-History, aber man möchte ja eigentlich einfach nur schnell einen Überblick gewinnen. Und vielleicht auch nicht über sich, sondern auch über andere user. Was liegt an einem verregneten Sonntag also näher, als einfach ein Skript zu schreiben, das die Webseite für einen durchgeht und die Daten einsammelt.

Die Grundlage dafür ist das Cmdlet Invoke-WebRequest, welches ein Objekt vom Typ „Microsoft.PowerShell.Commands.HtmlWebResponseObject“ zurück liefert. Dieses hat eine Eigenschaft „ParsedHtml“, die vom Typ „mshtml.IHTMLDocument2“ ist. Damit wären wir schon fast am Ziel, man muss einfach nur an den Perversitäten der Web-Entwickler und der verschahtelten Struktur der DIV-Tags vorbei kommen. In der Thread-Ansicht zum Beispiel hat die Vorschau auf jeden Beitrag die Klasse „threadsnippet“:

paul_thread_snippet

Den Inhalt bekommt man also mit

$page = Invoke-WebRequest -Uri "https://social.technet.microsoft.com/Forums/en-us/user/threads?user=Paul++Cunningham"
$html = $page.ParsedHtml
$html.all.tags("DIV") | foreach {
    if ($_.className) {
        if ($_.className.Trim() -eq "threadsnippet") {
            $snippet = $_
            foreach ($head in $snippet.all.tags("H3")) {
                $thread_topic = $head.innerText.Trim()
                foreach($link in $head.all.tags("A")) {
                    $thread_link = $link.href
                }
            }
        }
    }
}

Am Ende hat man dann in $thread_topic den Header und in $thread_link den Link zum gesamten Thread. Und so weiter.
Ich habe dafür zwei Skripte geschrieben, die in der PSGallery heruntergeladen werden können:

Export-TechNetContributionToCSV.ps1 geht die Beiträge des angegebenen Users durch und macht daraus eine CSV-Dateie mit diversen Informationen inklusive Links zu den einzelnen Threads. Schauen wir uns zum Beispiel die Contribution des bekannten Exchange-MVP Paul Cunningham an (man beachte die zwei Leerzeichen im TechNet-Usernamen):

tn_paul_export

Ist die CSV fertig, so kann man sie mit Measure-TechNetContribution.ps1 nach Foren oder sogar nach einzelnen Subforen zusammenfassen und, je nach Lust und Laune, tabellarisch oder sonst wie darstellen lassen:tn_paul_measureoder nach Hauptforen gegliedert:

tn_paul_measure_main

So kann man herausfinden, in welchen Wissensgebieten sich z.B. ein Kollege betätigt, der gern im TechNet Forum schreibt.

Tags » , , , , «

+