Haikos Blog Blog von Haiko Hertes zu allen Themen rund um Microsoft und Datacenter

17Jun/180

Office 365 – Verteiler eines Benutzers mittels PowerShell herausfinden

Immer mal wieder stell sich uns die Frage, in welchen Verteilern ein bestimmter Benutzer Mitglied ist. Natürlich kann man sich im Office365 Admin-Portal das Postfach ansehen, dort wird einem aufgelistet, wo der User Mitglied ist – allerdings werden hier nur die direkten Mitgliedschaften aufgelistet. In der Regel sind Verteiler / Distribution Lists aber in einander verschachtelt, um z.B. Team- / Gruppen- /Abteilungsstrukturen abzubilden. Um nun nicht von Hand alle Verteiler auf deren Mitgliedschaft in weiter oben liegenden Verteilern prüfen zu müssen, habe ich ein PowerShell-Skript geschrieben.

Dieses ermittelt zunächst, in welchen Verteilern der Benutzer unmittelbar enthalten ist und prüft dann diese Verteiler auf “Eltern”, also weiter oben stehende Verteiler, die den betreffenden Verteiler (und damit dann eben indirekt den Bnutzer) enthalten.

Hier ist das Skript:

[string[]]$global:AllreadyChecked = $null
 
function Get-DistributionGroupAllParents
{
    param ([string]$GroupName)
 
    [string[]]$Parents = $null
 
    ForEach ($Group in Get-DistributionGroup) 
    {
       If((Get-DistributionGroupMember $Group.Name | Where RecipientType -NE "UserMailbox").Name -contains $Groupname)
       {
          # Make sure, we are only checking every DGs parents once as many DG tree leafs may lead to the same parent DG
          If($global:AllreadyChecked -notcontains ($Group.Name))
          {
                $Parents += $Group.Name
                Get-DistributionGroupAllParents -GroupName $Group.Name
                $global:AllreadyChecked += $Group.Name
          }
       } 
    }
    Return $Parents
}
 
# Connect to Office 365
If((Get-PSSession | Where Computername –like "*ps.outlook.com*" | Where State –eq "Opened" | Measure-Object).Count -lt 1)
{
    $Credentials = Get-Credential –UserName "USER@DOMAIN.COM" -Message "Please enter your Office 365 / Azure AD password!"
    $ExchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.outlook.com/powershell -Credential $Credentials -Authentication Basic -AllowRedirection -WarningAction SilentlyContinue
    Import-PSSession $ExchangeSession -AllowClobber -DisableNameChecking >> $null
}
elseif((Get-PSSession | Where Computername -like "*ps.outlook.com*" | Where Availability -eq "Available" | Where State -ne "Opened" | Measure-Object).Count -ge 1)
{
    Import-PSSession -Session (Get-PSSession | Where Computername -like "*ps.outlook.com*" | Where Availability -eq "Available" | Select -Last 1)
}
 
# Get all DGs a user is member of
$User = Read-Host -Prompt "Enter User to check!" 
$Recipient = (Get-Recipient -RecipientType UserMailbox -Identity $User).Name
Write-Host "Script started discovery - this could take a while" -ForegroundColor Red
 
[string[]]$AllParents = $null
ForEach ($Group in Get-DistributionGroup) 
{
   If((Get-DistributionGroupMember $Group.Name).Name -contains $Recipient)  # Find all Groups the User is immediate member of:
   {
      $AllParents += $Group.Name
      Write-Host -ForegroundColor Green "$($Group.Name) ($($Group.PrimarySmtpAddress))"
      $AllParents += (Get-DistributionGroupAllParents -Group $Group.Name)
   } 
}
 
Write-Host "User is member of these groups (for some of them, he may be member through nested groups!):"
$AllParents.Where({ $_ -ne "" }) | select -uniq | sort | Get-DistributionGroup | ft DisplayName,Name,PrimarySmtpAddress -AutoSize

 

Viel Spaß damit!

11Jun/180

Azure – Application Settings von einem App Service zu einem anderen Kopieren

Wenn man beispielsweise für Entwicklung, Test und andere Zwecke Kopien einer Azure Web App benötigt oder zumindest initial die Settings übernehmen will, kann das, bei einer längeren Liste von Settings, ein recht hoher manueller Aufwand werden. Um diesen zu umgehen, habe ich ein entsprechendes PowerShell-Skript geschrieben. Dieses kopiert alle Settings und deren Werte von einer benannten Web App auf eine andere. Dabei sind die Ressource Groups und die Namen der Web Apps anzugeben.

Hier das Skript im Textlaut:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Adjust theese as needed
 
$SubscriptionId = "FILLINSUBSCRIPTIONID"
 
$ResourceGroupSource = "FILLINRGOFSOURCEWEBAPP"
$ResourceGroupTarget = "FILLINRGOFDESTWEBAPP"
 
$WebAppSource = "FILLINSOURCEWEBAPP"
$WebAppTarget = "FILLINDESTWEBAPP"
 
### no changes needed below ###
 
Connect-AzureRmAccount -Subscription $SubscriptionId
Set-AzureRmContext -SubscriptionId $SubscriptionId
 
$webAppSource = Get-AzureRmWebApp -ResourceGroupName $ResourceGroupSource -Name $WebAppSource 
 
# Get reference to the source app settings
$AppSettingsSource = $WebAppSource.SiteConfig.AppSettings
 
# Create empty Hash table variable for App Settings
$AppSettingsTarget = @{}
 
# Copy over all Existing App Settings to the Hash table
ForEach ($AppSettingSource in $AppSettingsSource) {
    $AppSettingsTarget[$AppSettingSource.Name] = $AppSettingSource.Value
}
 
# Save Connection Strings to Target Web App
Set-AzureRmWebApp -ResourceGroupName $ResourceGroupTarget -Name $WebAppTarget -AppSettings $AppSettingsTarget
Write-Host "Done!"

Viel Spaß damit!

28Mrz/180

Windows 10–Linux Subsystem installieren und ´Linux Distributionen aus dem Store nutzen

Seit einer Weile enthält Windows das “Subsystem für Linux” (WSL). Damit kann man Linux-Code nativ unter Windows ausführen, ohne dafür erst eine VM starten zu müssen oder ähnliches. Di Installation ist super einfach:

Am besten und einfachsten geht es per PowerShell (womit auch sonst? Smiley)

image

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux

Wenn man nun direkt die WSL startet (wsl.exe), dann bekommt man erstmal eine “Fehlermeldung”:

image

Anschließend kann man im Windows Store nach seiner Lieblings-Distribution suchen:

image

Zur Verfügung stehen:

  • Debian GNU/Linux
  • Ubuntu
  • OpenSUSE
  • SLES
  • Kali Linux

Der Start kann dann direkt aus dem Startmenü erfolgen:

image

Die Ersteinrichtung dauert einen Moment:

image

Abschließend muss noch ein User mit Passwort eingerichtet werden…

image

… und dann kann das Linux genutzt werden.

image

Viel Spaß beim Ausprobieren und Benutzen!

21Mrz/180

Windows Server – nächste Version offiziell angekündigt

Microsoft hat vor wenigen Stunden in einem längeren Blogartikel (siehe Link) viele Details zum nächsten Windows Server veröffentlicht. Dieser wird "Windows Server 2019" heißen und bereits in der zweiten Jahreshälfte diesen Jahres erscheinen. Über das Insider-Programm kann bereits eine Preview heruntergeladen und genutzt (natürlich nur zu Testzwecken!) werden:

https://insider.windows.com/de-de/for-business-getting-started-server/

Nach Aussage von Microsoft soll der neue Server vor allem 4 Hauptthemen adressieren:

  • Hybrid,
  • Security,
  • Application Platform und
  • Hyper-converged Infrastructure

Der neue Windows Server wird das nächste Release im LTSC (Long term servicing channel sein). In diesem wird es wieder eine GUI-Installationsoption sowie den Core-Server geben. Für den Semi-Annual-Channel (eher nicht für klassische Server-Szenarien geeignet) gibt es nur Core- und Nano-Server-Installationen.

Die Lizensierung des Server 2019 wird genau so wie bei 2016 organisiert sein. Microsoft kündigt aber eine Erhöhung der Preise für CALs an.

Details und weiteres siehe Blog-Artikel.

Quelle: https://cloudblogs.microsoft.com/windowsserver/2018/03/20/introducing-windows-server-2019-now-available-in-preview/

12Mrz/180

PowerShell Core und Jenkins – Continuous Integration für PowerShell auf Linux

In letzter Zeit beschäftige ich mich immer mal wieder mit DevOps-Themen. Eines davon ist Continuous Integration und das dafür gemachte Jenkins. Einige meiner Erfahrungen hierzu möchte ich gerne teilen.

Dieser Artikel beschäftigt sich mit PowerShell Core und Jenkins. In einem vorherigen Artikel habe ich in Kurzform erläutert, wie man Jenkins unter Debian Linux installiert. Das ist für diesen Artikel eine Art Voraussetzung.

Auch ohne PS Core kann Jenkins PowerShell-Code und –Skripte ausführen – allerdings braucht es dazu immer einen sogenannten “Build Slave” (also einen weiteren Jenkins-Knoten) mit Windows als Betriebssystem,

Mit der Verfügbarkeit von PowerShell Core für Linux ist es künftig nicht mehr zwingend nötig, einen Windows Buildslave zu verwenden, wenn man PowerShell benutzen möchte. Was leider NICHT funktioniert (zumindest mit Stand Heute), ist das PowerShell-Plugin für Jenkins. Dieses ruft den Code in einer Art und Weise auf, wie es nur unter Windows funktioniert. Selbst wenn man den dort verwendeten Aufruf “powershell.exe” mittels symbolic Link oder Alias auf pwsh mappt, wird es nicht funktionieren.

Ein möglicher Weg führt aber über den Einsatz eines Batch-Skriptes. Und das könnte so aussehen:

jenkins9
jenkins10

Mit Hilfe von Parametern kann später flexibler mit dem Build Job umgegangen werden (siehe weiter unten):

jenkins11

Der Einfachheit halber sollte der Parametername keine Leer- oder Sonderzeichen enthalten.

Für den eigentlichen Build (also die Aktionen, die durch den Job auszuführen sind), muss man hier nun auf “Shell ausführen” zurückgreifen. Der Menüpunkt “Windows PowerShell” wird nicht funktionieren (er steht auch nur zur Verfügung, wenn das Plugin bereits installiert ist).

jenkins12

Mein Aufruf für die Shell lautet hier beispielhaft:

pwsh -command "(Get-Variable -Name ${VariableName}).Value"

Mit ${PARAMETER} kann man auf den oben definierten Parameter zurückgreifen.

jenkins17

Anschließend kann man den Buildjob über “Bauen mit Parametern” aufrufen. Dabei kann man dann den Wert des/der Parameter definieren (oder, falls man Defaultwerte definiert hat, diese einfach beibehalten):

jenkins18

Anschließend kann man mit einem Klick auf den kleinen Pfeil neben dem Buildjob unten bei “Build-Verlauf” die “Konsolenausgabe” aufrufen:

jenkins19

Dort sieht man dann entweder den/die Fehler, falls welche aufgetreten sind, oder eben die Ausgabe des Jobs:

jenkins20

9Mrz/180

Jenkins auf Linux für Continuous Integration

In letzter Zeit beschäftige ich mich immer mal wieder mit DevOps-Themen. Eines davon ist Continuous Integration und das dafür gemachte Jenkins. Einige meiner Erfahrungen hierzu möchte ich gerne teilen.

In diesem Artikel möchte ich kurz beleuchten, wie man Jenkins auf Debian 9 “Stretch” installieren kann. Es gibt sicherlich einige andere Wege, aber dieser hier funktioniert sehr schnell und zuverlässig.

Als erstes braucht ihr natürlich eine Debian Maschine. Ich nutze dafür gerne Hyper-V oder Azure, aber jede andere Plattform wird ebenso funktionieren. Wichtig ist am Ende eigentlich nur, dass die Maschine Internetzugriff hat.

Als nächstes braucht man die passenden Keys und eine Quelle für APT:

1
2
wget -q -O - http://pkg.jenkins-ci.org/debian-stable/jenkins-ci.org.key | sudo apt-key add
echo "deb http://pkg.jenkins-ci.org/debian-stable binary/" | sudo tee /etc/apt/sources.list.d/jenkins.list

Nach einem anschließenden “apt-get update” kann man nun Jenkins installieren:

1
2
3
sudo apt-get update
sudo apt-get install jenkins -y
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

Der letzte Aufruf gibt das Initialpasswort aus, welches man anschließend für die Einrichtung benötigt.

Nun kann man unter

http://HOSTNAME:8080

den frisch installierten Jenkins aufrufen und einrichten:

jenkins1

Hier wird nun also der vorhin ausgelesene Key benötigt.

jenkins2

Bei den Plugins kann man im Zweifel erstmal mit den “suggested plugins” beginnen, aber ich bevorzuge es, einige nicht benötigte Plugins gar nicht erst zu installieren und wähle daher “Select plugins to install”. Meine konkrete Auswahl kann man in Teilen unten sehen:

jenkins3

Nun muss man noch ein Admin-Konto anlegen und dann kann es auch schon losgehen…

jenkins4

7Mrz/180

Azure Cloud Shell – PowerShell für Azure

Auch wenn es nicht erst seit gestern verfügbar ist, wollte ich gern auf ein cooles Feature in Azure hinweisen: Azure Cloud Shell.

Kurz gesagt handelt es sich hier um ein Zugang zu Azure und seine Funktionen über PowerShell oder Linux Bash. Diese Shell läuft in einem Container auf Azure und kann ganz einfach über den Browser verwendet werden. Dazu ruft man einfach

https://shell.azure.com

auf. Nach der Anmeldung muss einmalig ein Storageaccount eingerichtet werden, der dann in die Shell gemountet wird

und dort als Speicher zur Verfügung steht:

image

Nach der Anmeldung bei Azure muss die gewünschte Subscription ausgewählt werden:

image

Dann erfolgt die Auswahl der gewünschten Shell – Bash auf Linux oder PwerShell auf Windows:

image

Außerdem muss ein Storageaccount ausgewählt oder angelegt werden, wozu die Auswahl des gewünschten Abonnements erfolgen muss:

image

image

Das Anlegen des Storage Accounts und der erste Start der Shell dauern dann leider etwas…

image

image

Fortan steht die Shell auch etwas schneller zur Verfügung. Sie kann auch aus dem Azure Portal aufgerufen werden – dazu ist der Button in der oberen Leiste gedacht:

image

Es gilt, ein paar Kleinigkeiten zu beachten:

  • Es gibt einen 20-Minuten-Timeout in der Shell. Nach 20 Minuten Inaktivität wird die Shell recycelt.
  • Änderungen im Dateisystem sind nur innerhalb von $Home und clouddrive persistent
  • Man kann jederzeit zwischen Bash und PowerShell umschalten – aber man kann immer nur eine Shell gleichzeitig laufen haben
  • Der Power-Button oben startet die Shell (bzw. deren Container) neu – dabei gehen dann z.B. die Variablen und deren Werte verloren
  • Das Starten einer auf PowerShell basierenden Cloudshell kann ca. 60 Sekunden dauern – Bash geht deutlich schneller

Weitere Details siehe hier:

https://docs.microsoft.com/en-us/azure/cloud-shell/overview

6Mrz/181

Win32 OpenSSH unter Windows 10 1709 aktivieren und nutzen

Im Zuge der Weiterentwicklung von PowerShell Core und der Ausrichtung auf Linux-Systeme hat Microsoft eine neue Strategie für die Remote-Verwaltung von Systemen entwickelt. Diese beinhaltet u.a. die Verwendung von OpenSSH unter Windows. Das an sich ist nicht wirklich neu – zumindest etwas neuer ist aber die Tatsache, dass diese Technologie jetzt seit dem 1709 Build (“Fall Creators Update”) als Beta im System integriert ist. Allerdings müssen die Komponenten bei Bedarf noch aktiviert / installiert werden. Und das geht so:

Startmenü / Einstellungen (Zahnrad) / Apps / Optionale Features verwalten / Feature hinzufügen:

image

image

Dort finden sich dann die beiden OpenSSH Optionen:

image

Beide Optionen sollten eigentlich selbst-erklärend sein…

Alternativ kann die Installation auch über PowerShell erfolgen:

# Installieren von Server und Client
Add-WindowsCapability -Online -Name OpenSSH*
# Nur Server
Add-WindowsCapability -Online -Name OpenSSH.Server*
# Nur Client
Add-WindowsCapability -Online -Name OpenSSH.Client*

image

image

Der SSH Client läuft jetzt völlig problemlos von der Kommandozeile – wenn man den vollen Pfad benutzt:

C:\Windows\System32\OpenSSH\ssh.exe

Spätestens nach dem nächsten Neustart ist dann der Pfad auch in PATH und damit von überall aus aufzurufen.

image

Die Konfiguration des OpenSSH-Servers ist ein kleines bisschen aufwändiger. Diesem Thema werde ich demnächst einen weiteren Blogartikel widmen. Stay tuned!

30Jan/180

PowerShell & Fotobox – Fotos zu einem bestimmten Hashtag von Instagaram laden

Auf meiner in den letzten Wochen und Monaten gebauten Fotobox (Mehr dazu hier) gibt es einen extra Bildschirm für eine Slideshow der aufgenommenen Fotos. Diese zeigt die neuesten x Fotos aus der Liste aller aufgenommenen Bilder, wobei ein neues Foto immer das älteste Bild ablöst (zu der technischen Realisierung hier werde ich später noch einen größeren Artikel schreiben).

Nun kam mir in diesem Zusammenhang die Idee, aus den sozialen Medien ebenfalls die aktuellsten Bilder zu einem bestimmten Hashtag abzurufen, damit die Gäste unter diesem Hashtag Handyfotos posten könnten, die dann wiederum in die Slideshow einfließen sollten. Hierzu gibt es ein paar Lösungen im Internet, die aber fast alle recht teuer sind und auch größtenteils nur im Browser ablaufen. Ich wollte aber gern die einzelnen Dateien lokal auf dem Rechner haben. Also habe ich mir ein PowerShell-Skript geschrieben, welches genau diese Anforderung erfüllt – allerdings vorerst nur für Instagram.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# What are you searching for?
$Hashtag = "PUTYOURHASHTAGHERE"
 
# How long to wait for the next retry - set to 0 to only search once
$SecondsBetweenRequests = 10
 
# Where do you want to store the downloaded pictures? Use no "\" at the end!
$WhereToStorePictures = "D:\Pictures\" 
 
### Don't change anything below here unless you know what you are doing! ###
 
# If the destination folder does not exist, we need to create it first
If(!(Test-Path $WhereToStorePictures -PathType Container)) 
{
    New-Item $WhereToStorePictures -ItemType Directory
}
 
$URI = "https://www.instagram.com/explore/tags/$Hashtag/"
 
# We are running this from now one until CTRL-C is pressed
Write-Host "To stop the process, press CTRL+C - otherwise it runs forever!" -ForegroundColor Yellow
While($true)
{
    # Get the whole content according to the hashtag search
    $HTML = Invoke-WebRequest -Uri $URI 
 
    # Get all the filenames of pictures with that hashtag from the HTML
    $URLs = ($HTML.Content | Select-String -Pattern '"display_url":\s.*?"edge_liked_by"' -AllMatches).Matches.Value | ForEach-Object {$_.Substring(16,$_.Length-34)}
 
    # Iterate through all the pics on the website
    ForEach($URL in $URLs)
    {
        # We need the filename to see if it was allready downloaded
        $Filename = $URL.Substring($URL.LastIndexOf('/')+1,$URL.length-$URL.LastIndexOf('/')-1) 
 
        # If the pic is not allready in the destination folder, download it
        If(!(Test-Path -PathType Leaf -Path "$WhereToStorePictures\$Filename"))
        {
            Write-Host "`nNew Picture found!" # You can remove this if you want
            Start-BitsTransfer $URL -Destination "$WhereToStorePictures\" # The actual download
        }
    }
    If($SecondsBetweenRequests -eq 0) # Then we just do the download-thing once
    {
        break
    }
    else
    {
        Start-Sleep -Seconds $SecondsBetweenRequests
        Write-Host "." -NoNewline # Just to let you know it is still working... - you can remove this if you want
    }
 
}

 

Das Skript ist wie üblich auch im Microsoft Script Center zu finden: https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Get-Instagram-7d9ddb44

Viel Spaß beim Ausprobieren oder adaptieren!

26Jan/180

Hyper-V & PowerShell – Kerberos Delegierung für LiveMigration für mehrere Server gegenseitig eintragen

Hinweis: Wer mehr zu diesem Thema wissen möchte, kann gerne meinen ausführlichen Artikel zu diesem Thema lesen!

Nichts neues, aber da es mir heute wieder über den Weg gelaufen ist: Wenn man mittels Hyper-V Manager eine VM von einem Host auf einen anderen live verschieben, dann gibt es im Wesentlichen zwei mögliche Protokolle: CredSSP und Kerberos.

kerberos1

CredSSP ist in der Anendung grundsätzlich einfach, hat aber einen entscheidenden Nachteil: Man muss interaktiv (!) an dem Host angemeldet sein, von dem man die VM (weg) verschieben will. Andernfalls bekommt man einen hübschen Fehler:

kerberos2kerberos3

Der Fehlertext lautet:

Fehler beim Herstellen einer Verbindung mit dem Host “DERHOSTNAME”: Die Anmeldeinformationen, die dem Paket übergeben wurden, wurden nicht erkannt. (0x8009030D).

Failed to establish a connection with host “THEHOSTNAME”: No credentials are available in the security package (0x8009030E)

Insbesondere auf Core-Servern bleibt hier also nur eine Möglichkeit: Man muss auf Kerberos umstellen! Dazu jedoch müssen sich die Server, zwischen denen man verschieben möchte, für das Protokoll “cifs” und “Microsoft Virtual System Migration Service” vertrauen.

Das könnte man jetzt im ActiveDirectory manuell konfigurieren – aber spätestens bei einer zweistelligen Anzahl an Hosts ist das eine sehr mühsame Klickerei. Also warum nicht PowerShell bemühen? Ich habe mir dazu schon vor einer ganzen Weile ein recht kurzes Skript geschrieben, was jeden der aufgeführten Host gegen jeden anderen berechtigt bzw. die Kerberos-Delegierung einrichtet.

Hier das Skript im Textlaut. Viel Spaß damit!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$Domain = 'api-leipzig.local'
 
# Actually not needed anymore, but who knows...
Import-Module ActiveDirectory
# Put the NetBIOS names of your hosts here
$HyperVHosts = "HOST1","HOST2","HOST3","HOST4","HOST5"
 
ForEach($Host1 in $HyperVHosts)
{
    ForEach($Host2 in $HyperVHosts)
    {
        If($Host1 -ne $Host2)
        {
            "Delegating from $Host1 to $Host2..."
            Get-ADComputer $Host1 | Set-ADObject -Add @{"msDS-AllowedToDelegateTo" = "Microsoft Virtual System Migration Service/$($Host2).$($Domain)", "cifs/$($Host2).$($Domain)", "Microsoft Virtual System Migration Service/$Host2", "cifs/$Host2"}
            Get-ADComputer $Host1 | Set-ADAccountControl -TrustedForDelegation:$false -TrustedToAuthForDelegation:$true
        }
    }
}