RSAT-Installation unter Win10 Build 1809 per Powershell

Hier ist eine Auflistung der RSAT-Features, welche unter Windows 10 Build 1809 per Powershell installiert werden können

RSAT: Tools für Active Directory Domain Services und Lightweight Directory Services

Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0

RSAT: Verwaltungshilfsprogramme für die BitLocker-Laufwerkverschlüsselung

Add-WindowsCapability -Online -Name Rsat.BitLocker.Recovery.Tools~~~~0.0.1.0

RSAT: Active Directory Certificate Services Tools

Add-WindowsCapability -Online -Name Rsat.CertificateServices.Tools~~~~0.0.1.0

RSAT: DHCP-Servertools

Add-WindowsCapability -Online -Name Rsat.DHCP.Tools~~~~0.0.1.0

RSAT: DNS-Servertools

Add-WindowsCapability -Online -Name Rsat.Dns.Tools~~~~0.0.1.0

RSAT: Failoverclustering-Tools

Add-WindowsCapability -Online -Name Rsat.FailoverCluster.Management.Tools~~~~0.0.1.0

RSAT: Tools für Dateidienste

Add-WindowsCapability -Online -Name Rsat.FileServices.Tools~~~~0.0.1.0

RSAT: Group Policy Management Tools

Add-WindowsCapability -Online -Name Rsat.GroupPolicy.Management.Tools~~~~0.0.1.0

RSAT: IP-Adressverwaltungsclient (IPAM)

Add-WindowsCapability -Online -Name Rsat.IPAM.Client.Tools~~~~0.0.1.0

RSAT: Data Center Bridging LLDP-Tools

Add-WindowsCapability -Online -Name Rsat.LLDP.Tools~~~~0.0.1.0

RSAT: Netzwerkcontroller-Verwaltungstools

Add-WindowsCapability -Online -Name Rsat.NetworkController.Tools~~~~0.0.1.0

RSAT: Tools für Netzwerklastenausgleich

Add-WindowsCapability -Online -Name Rsat.NetworkLoadBalancing.Tools~~~~0.0.1.0

RSAT: Tools für die Remotezugriffsverwaltung

Add-WindowsCapability -Online -Name Rsat.RemoteAccess.Management.Tools~~~~0.0.1.0

RSAT: Remote Desktop Services Tools

Add-WindowsCapability -Online -Name Rsat.RemoteDesktop.Services.Tools~~~~0.0.1.0

RSAT: Server-Manager

Add-WindowsCapability -Online -Name Rsat.ServerManager.Tools~~~~0.0.1.0

RSAT: Abgeschirmte VM-Tools

Add-WindowsCapability -Online -Name Rsat.Shielded.VM.Tools~~~~0.0.1.0

RSAT: Storage Migration Service Management-Tools

Add-WindowsCapability -Online -Name Rsat.StorageMigrationService.Management.Tools~~~~0.0.1.0

RSAT: Storage Replica Module for Windows PowerShell

Add-WindowsCapability -Online -Name Rsat.StorageReplica.Tools~~~~0.0.1.0

RSAT: System Insights-Modul für Windows PowerShell

Add-WindowsCapability -Online -Name Rsat.SystemInsights.Management.Tools~~~~0.0.1.0

RSAT: Volume Activation Tools

Add-WindowsCapability -Online -Name Rsat.VolumeActivation.Tools~~~~0.0.1.0

RSAT: Windows Server Update Services-Tools

Add-WindowsCapability -Online -Name Rsat.WSUS.Tools~~~~0.0.1.0

Powershell: Abfrage des AD-User-Passwortalters

Im Rahmen einer Entstörung von AD-Benutzeraccounts benötigte ich die aktuelle Passwort-Daten beim Anruf der Benutzer. Dazu habe ich mir ein kleines Skript geschrieben. Dieses berücksichtigt die folgenden Szenarien:

  • AD-Benutzer gibt es nicht
  • AD-Benutzer ist deaktiviert
  • AD-Benutzer hat ein Ablaufdatum und dieses ist überschritten
  • AD-Benutzer muss sein Kennwort bei der nächsten Anmeldung ändern
  • AD-Benutzer hat keinen Passwort-Ablauf
  • AD-Benutzer ist gesperrt

Wenn dieses alles nicht zutrifft werden folgende 2 Definitionen zur Berechnung des Passwortalters herangezogen:

  • AD-Benutzer nutzt die Default Domain Policy
  • AD-Benutzer nutzt eine Fine-grained Password Policy
Import-Module ActiveDirectory -Force
 
If ($args)
{
$Userlogin = $args[0]
}
 
else
 
{
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
 
$form = New-Object System.Windows.Forms.Form
$form.Text = 'Passwort-Display'
$form.Size = New-Object System.Drawing.Size(300,200)
$form.StartPosition = 'CenterScreen'
 
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = 'OK'
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)
 
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = 'Cancel'
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
 
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Bitte sAMAccountName eingeben:'
$form.Controls.Add($label)
 
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,40)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($textBox)
 
$form.Topmost = $true
 
$form.Add_Shown({$textBox.Select()})
$result = $form.ShowDialog()
 
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
    $Userlogin = $textBox.Text
}
 
if ($result -eq [System.Windows.Forms.DialogResult]::Cancel)
{
    exit
}
 
}
 
$Usercheck = Get-Aduser -filter {samaccountname -eq $userlogin}
 
If ($usercheck -eq $NULL)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Der Benutzer existiert nicht.","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
 
$employee = Get-AdUser $Userlogin -Properties enabled,PasswordNeverExpires,PasswordLastSet,Displayname,LockedOut,"msDS-UserPasswordExpiryTimeComputed",AccountExpirationDate
 
If ($employee.enabled -like "False")
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Benutzer "+$employee.displayname+" ist deaktiviert","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
 
If (($employee.AccountExpirationDate) -eq $null)
{
}
else
{
 
$Ablaufdatum = ((get-date) - ($employee.AccountExpirationDate))
$Ablauftage=$Ablaufdatum.days
 
If ($Ablauftage -gt "0")
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+ "Ablaufdatum des Accounts: "+$employee.AccountExpirationDate,"User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
 
}
 
 
If ($employee.PasswordLastSet -eq $null)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Das Passwort des Accounts muss bei der nächsten Anmeldung geändert werden.","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
 
If ($employee.passwordneverexpires -like "True")
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Das Passwort des Accounts läuft nicht ab.","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
 
If ($employee.LockedOut -like "True")
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Der Account ist gesperrt.","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Error)
exit
}
 
$FGPP=(Get-ADUserResultantPasswordPolicy $Userlogin)
 
If($fgpp.name -eq $null) 
{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$expireDate=((Get-adUser($Userlogin) -Properties PassWordLastSet).PasswordLastSet + (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Ticks)
$daysleft=($expireDate-(get-date)).Days
 
if($daysleft-le "0")
 
{
$daysnegative =-$daysleft
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Regelwerk: Default Domain Policy `n`n"+"Das Passwort ist seit "+<span style="display: inline !important; float: none; background-color: transparent; color: #2b2b2b; cursor: text; font-family: 'Philosopher',sans-serif; font-size: 16px; font-style: normal; font-variant: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration: none; text-indent: 0px; text-transform: none; -webkit-text-stroke-width: 0px; white-space: normal; word-spacing: 0px;">$daysnegative</span>+" Tagen abgelaufen. `n`n"+"Genauer Zeitstempel: "+$expireDate+"","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Warning)
}
 
else{
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Regelwerk: Default Domain Policy `n`n"+"Das Passwort muss in "+$daysleft+" Tagen geändert werden. `n`n"+"Genauer Zeitstempel: "+$expireDate+"","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Information)
}
exit
}
 
else{
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$expireDate=((Get-adUser($Userlogin) -Properties PassWordLastSet).PasswordLastSet + (Get-ADUserResultantPasswordPolicy -Identity $Userlogin).MaxPasswordAge.Ticks)
$daysleft=($expireDate-(get-date)).Days
 
if($daysleft-le "0")
 
{
$daysnegative =-$daysleft
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Regelwerk: "+$fgpp.Name+" `n`n"+"Das Passwort ist seit "+$daysnegative+" Tagen abgelaufen. `n`n"+"Genauer Zeitstempel: "+$expireDate+"","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Warning)
}
 
else{
[System.Windows.Forms.MessageBox]::Show("Benutzer: "+$employee.displayname+"  `n`n"+"Regelwerk: "+$fgpp.Name+" `n`n"+"Das Passwort muss in "+$daysleft+" Tagen geändert werden. `n`n"+"Genauer Zeitstempel: "+$expireDate+"","User " +$Userlogin,0,[System.Windows.Forms.MessageBoxIcon]::Information)
}
exit

Damit die Daten ordnungsgemäß abgefragt werden können, benötigt der Ausführende die Leseberechtigungen auf den Container „Password Setting Objects“ im System-Ordner der Domäne.

Das Skript kann per GUI sowie per Skript gestartet werden.

PowerShell: DHCP-Leases exportieren

Manchmal kann es Sinn ergeben, dass man die gezogenen Leases eines Servers ausgibt. Dieses ist mit einem kleinen Skript möglich:

Get-DHCPServerV4Scope 
| ForEach {
    Get-DHCPServerv4Lease -ScopeID $_.ScopeID
} 
| Select-Object IPAddress,HostName,ClientID,AddressState,LeaseExpiryTime 
| Export-Csv ".\$($env:COMPUTERNAME)-Reservations.csv" -NoTypeInformation

Dieses ist gedacht direkt auf dem Server aufgerufen zu werden. Dabei werden alle DHCP-Scopes berücksichtigt. Der Dateiname des Exports wird mit dem Servernamen als Start definiert.

Entsprechend werden aktive und inaktive Reservierungen neben den dynamisch verteilten IP-Adressen aufgelistet. Die aus dem jeweiligen Pool des Servers vergebenen Adressen werden inklusive dem Ablaufdatum ausgegeben.

 

Powershell: Ausdrucke pro Drucker ermitteln

Vor einer Umstellung oder auch einer Abschaltung eines Druckservers kann ein Audit der Druckdaten sinnvoll sein. Dabei wird ermittelt das Skript in den Parametern eines Zeitfensters, welcher Drucker wieviel Druckaufträge verarbeitet hat.

$Startzeit = "24.11.2018 00:00:00"
$Endzeit = "24.11.2018 23:59:59"
 
$ZentralDatei ="E:\Audit\Zentraldatei.csv"
$Auswertung ="E:\Audit\Auswertung.csv"
 
$Druckerliste = @()
 
$Resultate = Get-WinEvent -FilterHashTable @{LogName="Microsoft-Windows-PrintService/Operational"; ID=307; StartTime=$Startzeit; EndTime=$Endzeit;}
 
ForEach($Resultat in $Resultate){
  $Datensatz = [xml]$Resultat.ToXml()
  $DatenDetails = New-Object -TypeName psobject -Property @{
 
      Druckername = $Datensatz.Event.UserData.DocumentPrinted.Param5
      Zeitstempel = $Resultat.TimeCreated
 
      }
    $Druckerliste += $DatenDetails
  }
 
 
$Druckerliste | Export-Csv -LiteralPath $ZentralDatei -NoTypeInformation
 
 
Import-Csv $ZentralDatei | Group-Object Druckername | Select-Object Name, Count | Sort-Object -Property name | Export-csv $Auswertung -NoTypeInformation

Das eingesetzte CMDlet „Get-WinEvent“ steht seit Windows Server 2008 R2 und späteren Betriebssystemen zur Verfügung.

Um die Daten zu erhalten, muss allerdings in der Ereignisanzeige das Log für „Microsoft \ Windows \ Printservices \ Operational“ aktiviert worden sein. Dieses sollte auch vorzeitig durchgeführt worden sein, um längere Zeiträume zu überwachen. Die Daten können nicht rückwirkend erstellt werden. Anbei die schnelle Umsetzung per PowerShell-Aufruf:

$EventLogging = Get-WinEvent -ListLog 'Microsoft-Windows-PrintService/Operational'
$EventLogging.IsEnabled = $true
$EventLogging.MaximumSizeInBytes = 1000000
$Eventlogging.LogMode = "Circular"
$EventLogging.SaveChanges()

Mit diesem Befehlssatz wird das erforderliche Eintrag in der Ereignisanzeige aktiviert. Dabei wird ein 1 GigaByte großes Log erstellt. Wenn der Schwellwert erreicht ist, wird beim Hinzufügen eines neuen Ereignisses das Älteste entfernt.

In der Datei „Auswertung.csv“ stehen dann die Druckernamen sowie die Anzahl der Ausdrucke im Zeitraum ausgedruckt wurden.

Powershell: Persönliches Laufwerk bei AD-Benutzern setzen

In einem Projekt sollte das Benutzer-Verzeichnis aus dem Bereich des Loginskriptes entfernt und in das Benutzer-Objekt des Active Directory verlagert werden. Da es sich um eine 4-stellige der Anzahl sowie mehreren Verzeichnispfaden handelte, wollte ich dieses gern über PowerShell abarbeiten lassen.

Bei einem meiner Tests kam es dann zum Problem, dass nicht der geplante Laufwerksbuchstabe im Benutzerprofil genutzt wurde. Der letzte freie Buchstabe, in meinem Fall „Z:“, wurde in meinem Fall verbunden. Im AD-Objekt wurde auch der korrekte Buchstabe angezeigt. Bei dem hier korrekt abgebildeten Befehl hatte ich den Doppelpunkt „vergessen“:

Set-ADUser tester01 -HomeDrive "P:" -HomeDirectory "\\Server01\Freigabe\Home\tester01"

Dieses wird in PowerShell ohne Fehlermeldung angenommen. Aus diesem Grund fiel mir diese „Unschärfe“ erst beim Test auf.

PowerShell: Sonderzeichen in Pfaden nutzen

Am gestrigen Tag hatte ich die Aufgabe bei einem Ordnerpfad eine Veränderung vorzunehmen. Dieses hatte ich auch bereits für andere Ordner auf dem Dateisystem vorgenommen. Somit nahm ich meinen Befehlsaufruf zur Hand. Schon beim Hinzufügen des Ordnerpfades in der PowerShell ISE merkte ich, dass etwas nicht stimmte.

Aufgrund eines Dollarzeichens in der Pfadangabe erwartete der Befehl nun die Angabe einer Variable. Ich hatte allerdings keine Lust in Zukunft für eine Handvoll Ordner erst Variablen zu definieren. Der Pfad war ja eigentlich korrekt. Die Interpretation von PowerShell störte in diesem Fall.

Nach ein bisschen experimentieren fand ich heraus, dass ein „Gravis“ (engl.: Backtick) mein Problem lösen kann:

#Defekte Pfade
Get-childitem \\server\Freigabe\Ordner$Data1
Get-childitem \\server\Freigabe\Ordner$Data2
Get-childitem \\server\Freigabe\Ordner$Data3
 
#Funktionierende Pfade
Get-childitem \\server\Freigabe\Ordner`$Data1
Get-childitem \\server\Freigabe\Ordner`$Data2
Get-childitem \\server\Freigabe\Ordner`$Data3

Mit diesem Zeichen definiert man, dass das Sonderzeichen ignoriert wird. Die Variable wird somit zum Pfad-Bestandteil. Dieses funktioniert auch bei weiteren Sonderzeichen wie zum Beispiel Klammern.

PowerShell: Anmeldung an MS Online Services

Microsoft bietet verschiedene Services unter Office 365 an. Diese können neben der Webapplikation auch per PowerShell angesprochen werden. Die verschiedenen Dienste über eine PowerShell-Session, wenn man die folgendes Skript nutzt:

$domainHost="domain"
$credential = Get-Credential
 
 
#Verbindungsaufbau über Azure Active Directory PowerShell for Graph module
#Connect-AzureAD -Credential $credential
 
#Verbindungsaufbau über Azure Active Directory Module for Windows PowerShell module
#Connect-MsolService -Credential $credential
 
 
Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
Connect-SPOService -Url https://$domainHost-admin.sharepoint.com -credential $credential
Import-Module SkypeOnlineConnector
$sfboSession = New-CsOnlineSession -Credential $credential
Import-PSSession $sfboSession
$exchangeSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $credential -Authentication "Basic" -AllowRedirection
Import-PSSession $exchangeSession
$SccSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://ps.compliance.protection.outlook.com/powershell-liveid/" -Credential $credential -Authentication "Basic" -AllowRedirection
Import-PSSession $SccSession -Prefix cc

Zur Nutzung der Befehlsauflistung muss die Variable „$domainHost gegen die persönlich definierte Microsoft-Domain ausgetauscht werden.
Für die Verbindung zu Domain muss der jeweilig gewünschte AD-Connector durch die Entfernung des # freigeben.

Weitere Informationen sind bei Microsoft zu finden