Private/Wissen/C_Scripting/C11_Sicherheit.ps1

# ? TITEL Sicherheit
# ? DESCRIPTION PowerShell absichern und mit Sicherheit in der PowerShell umgehen
# ? TAGS Sicherheit Security PKI NTFS Security Crypt Decrypt Signature Credentials ActiveDirectory GPO ScriptBlockLogging Transcript CLM PSRC PSSC
# ? VERSION 2020.01.28

# TODO Weiterführende und Nachschlage-Informationen
# TODO siehe C02_Ausführungsrichtlinien.ps1, C08_ScripteSignieren.ps1, X11_Technology_Hacking.ps1
# TODO Vergleich Sicherheitsmechanismen div. Script-Sprachen https://devblogs.microsoft.com/powershell/a-comparison-of-shell-and-scripting-language-security/
# TODO betroffene about-Seiten:
Get-Help -Name about_Execution_Policy -ShowWindow

<#
    ! Die PowerShell ist im System verwurzelt. Daher sorgt ein generelles Blockieren nur für schein­bare Sicherheit.
    ! Den besten Schutz versprechen letztlich die Schutz­mechanismen von PowerShell selbst.
    ! Neben Mitteln, die den Missbrauch von PowerShell unterbinden, stehen auch solche zur Verfügung,
    ! um verdächtigen und unerwünschten Aktivitäten zu protokollieren und diese auf die Spur zu kommen.
#>


# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# !!! Unbedingt sollten Sie PowerShell 2.0 entfernen. Mit dieser alten Shell lassen sich alle !!!
# !!! wesentlichen Restriktionen für PowerShell unterlaufen. Das optionale Feature ist !!!
# !!! vorinstalliert und kann aber ab Windows 8.1 und Server 2012 deinstalliert werden. !!!
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

#region Zufallszahlen & -text erzeugen

# ? Zufallstext z.B. für Initialpasswort:
[System.IO.Path]::GetRandomFileName() -replace "\.", [String]::Empty

# ? Zufallszahlen:
1..100 | Get-Random -Count 3
(1..100 | Get-Random -Count 3 | ForEach-Object {"{0:00}" -f $_}) -join [String]::Empty
[Convert]::ToBase64String((0..255 | Get-Random -Count 9))

#endregion

#region SecureString

# ? betroffene Cmdlets:
Get-command -Noun SecureString -Module Microsoft.PowerShell.Security

# ? Ein Passwort verschlüsselt aufbewarben und sicher einlesen verwenden:
Read-Host -Prompt "Passwort eingeben" -AsSecureString | 
    ConvertFrom-SecureString | 
    Out-File -FilePath "C:\temp\SecureString.txt" -Force

Get-Content -Path "C:\Temp\SecureString.txt" | ConvertTo-SecureString 

#endregion

#region Credentials

# ? Credential sicher im RAM aufbewahren:
$cred = Get-Credential -Message "Zugangsdaten für Administrator auf ExServer4711" -UserName "Administrator"

# ? Credential verschlüsselt speichern:
$username = Read-Host -Prompt "Benutzername eingaben"
$password = Read-Host -Prompt "Passwort für $username eingeben" -AsSecureString
[PSCustomObject]@{
    Username = $username
    Password = $password | ConvertFrom-SecureString
} | ConvertTo-Json | Set-Content -Path C:\Temp\Credential_Admin.json -Force
$admin = Get-Content -Path C:\Temp\Credential_Admin.json | ConvertFrom-Json | Select-Object Username, @{Label="Password"; Expression={$_.Password | ConvertTo-SecureString}}
$cred = [System.Management.Automation.PSCredential]::new($admin.Username, $admin.Password)
Enter-PSSession -ComputerName "127.0.0.1" -Credential $cred

#endregion

#region PKI

# TODO Siehe C08_ScripteSignieren.ps1

# ? betroffene Cmdlets:
Get-Command -Name * -Module PKI

#endregion

#region Zeichenketten verschlüsseln

# ? betroffene Cmdlets:
Get-Command -Noun "CmsMessage" -Module "Microsoft.PowerShell.Security"

# ! Siehe auch: CipherNet Lite => Funktionsbibliothek zum Thema Verschlüsselung

$params = @{
    Subject           = "CN=_Doctor Crypt (Test-Verschlüssler), E=d.crypt@test.local"
    HashAlgorithm     = "SHA512"
    KeyExportPolicy   = [Microsoft.CertificateServices.Commands.KeyExportPolicy]::ExportableEncrypted
    KeyLength         = 4096
    KeySpec           = [Microsoft.CertificateServices.Commands.KeySpec]::KeyExchange
    CertStoreLocation = "Cert:\CurrentUser\My"
    Type              = [Microsoft.CertificateServices.Commands.CertificateType]::DocumentEncryptionCert
    NotAfter          = (Get-Date).AddMinutes(60)
}
$myPfxCert = New-SelfSignedCertificate @params

$cryptedText = "Hallo Würzburg!" | Protect-CmsMessage -To $myPfxCert.Thumbprint
$cryptedText
$cryptedText | Unprotect-CmsMessage -To $myPfxCert.Thumbprint

Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object -Property Thumbprint -eq -Value $myPfxCert.Thumbprint | Remove-Item

#endregion

#region Umgang mit Zugriffsberechtigungen

# ! Die PowerShell-Board-Mittel sind leider sehr überschaubar. ...

# ? betroffene Cmdlets:
Get-Command "*acl*" -Module "Microsoft.PowerShell.Security"

# ? Berechtigungen von Ordner-A auf Ordner-B übetragen:
New-Item -Path "c:\" -Name "TempTest" -ItemType Directory -Force
Get-Acl -Path "C:\Temp" | Select-Object -Property "*"
Get-Acl -Path "C:\Temp" | Set-Acl -Path "C:\TempTest"

# ! ... Umfassende /aufwendige Möglichkeiten ergeben sich mit dem .NET. ...
# ! ... oder Sie schauen Sich das Module NTFSSecurity aus der PowerShallGallery einmal an:

Install-Module -Name "NTFSSecurity" -SkipPublisherCheck -Scope "CurrentUser" -Force -PassThru -Verbose
Get-command -Module "NTFSSecurity"
Get-NTFSAccess -Path "C:\Temp"

#endregion

#region Scripte Signieren

# TODO siehe C08_ScripteSignieren.ps1, X21_Technology_PKI.ps1

$MyCert = Get-PfxCertificate -FilePath ".\AKPT\Private\Wissen\X_Technology\X98_Kryptonite.pfx"
$MyCert | Export-Certificate -FilePath "C:\Temp\Kryptonite.cer"
Import-Certificate -FilePath "C:\Temp\Kryptonite.cer" -CertStoreLocation "Cert:\LocalMachine\Root"
"' > > > Hallo Welt! < < < ' | Write-Host -ForegroundColor Red -BackgroundColor Yellow" | Set-Content -Path "c:\Temp\Get-HalloWelt.ps1" -Force

# ! Datei signieren:
$params = @{
    Certificate     = $MyCert
    FilePath        = "C:\Temp\Get-HalloWelt.ps1"
    TimestampServer = "http://timestamp.globalsign.com/scripts/timstamp.dll"
    IncludeChain    = "all"
    HashAlgorithm   = "SHA512"
    Force           = $true

}
Set-AuthenticodeSignature @params

# ! Signierte Datei validieren:
"C:\Temp\Get-HalloWelt.ps1" | Get-AuthenticodeSignature | Select-Object -Property *

#endregion

#region Ausführungsrichtlinien

# TODO siehe C02_Ausführungsrichtlinien.ps1
# TODO betroffene about-Seiten:
Get-Help -Name "about_Execution_Policy" -ShowWindow

# ? Wie ist die ExecutionPolicy eingestellt:
Get-ExecutionPolicy -List

# ? Wie ist die ExecutionPolicy für den aktuellen Script-Host eingestellt:
$env:PSExecutionPolicyPreference

# ! Set-ExecutionPolicy steuert folgende Registry-Properties:
Get-ItemPropertyValue -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" -Name "ExecutionPolicy"
Get-ItemPropertyValue -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\ShellIds\ScriptedDiagnostics"  -Name "ExecutionPolicy"
Get-ItemPropertyValue -Path "Registry::HKEY_CURRENT_USER\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell"  -Name "ExecutionPolicy"

# ? Empfohlene Einstellung für Client und Server
Set-ExecutionPolicy -ExecutionPolicy AllSigned # TODO siehe: Get-Help -Name about_Signing -ShowWindow
# ! d.h. alle ausführbare Dateien müssen signiert (x.509-Zertifikat) sein

# ? Empfohlene Einstellung für Test-/Entwicklungszwecke (Remote-Scripte müssen signiert sein)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope LocalMachine -Force
# ! Bzgl. RemoteSign werden Dateien an einem Download-Marker identifiziert! siehe Cmdlet: Unblock-File

#endregion

#region PowerShell in der ActiveDirectory-Domäne per GPO absichern

<#
 
! EdgeUI.admx
    ? Prevent users from replacing the Command Prompt with Windows PowerShell in the menu
    ? they see when they right-click the lower-left corner or press the Windows logo key+X
    User - Windows Components\Edge UI
    HKCU\Software\Policies\Microsoft\Windows\EdgeUI!ShowCommandPromptOnWinX
 
! PowerShellExecutionPolicy.admx
    ? Turn on PowerShell Script Block Logging
    Machine - Windows Components\Windows PowerShell
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
 
    ? Turn on PowerShell Script Block Logging
    User - Windows Components\Windows PowerShell
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockLogging
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging!EnableScriptBlockInvocationLogging
 
    ? Turn on PowerShell Transcription
    Machine - Windows Components\Windows PowerShell
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscripting
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!OutputDirectory
    HKLM\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader
 
    ? Turn on PowerShell Transcription
    User - Windows Components\Windows PowerShell
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableTranscripting
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!OutputDirectory
    HKCU\Software\Policies\Microsoft\Windows\PowerShell\Transcription!EnableInvocationHeader
 
! Scripts.admx
    ? Run Windows PowerShell scripts first at computer startup, shutdown
    Machine - System\Scripts
    HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System!RunComputerPSScriptsFirst
 
    ? Run Windows PowerShell scripts first at user logon, logoff
    Machine - System\Scripts
    HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System!RunUserPSScriptsFirst
 
    ? Run Windows PowerShell scripts first at user logon, logoff
    User - System\Scripts
    HKCU\Software\Microsoft\Windows\CurrentVersion\Policies\System!RunUserPSScriptsFirst
#>


#endregion

#region ScriptBlockLogging (Protokollierung von PowerShell-Scriptblöcken)

# ? Variante A: Host-Ein-/Ausgabe protokollieren per Start-Transcript:

Start-Transcript -Path "C:\Temp\PsHost.log" -Append -Force # ! Transcript starten
Get-Process                                                # ! Test Eingabe & Ausgaben
Read-Host -Prompt "Passwort eingeben" -AsSecureString      # ! Test Eingabe & Ausgaben
Stop-Transcript -Verbose                                   # ! Transcript stoppen
Start-Process -FilePath "C:\Temp\PsHost.log"               # ! Log-Datei anzeigen

# ? Variante B: Permanentes und sicheres Protokollieren sämtlicher Host's aktivieren (auch per GPO)
# ? Per GPO: Computerkonfiguration / Administrative Vorlagen / Windows-Komponenten /
# ? Windows PowerShell / Protokollierung von PowerShell-Scriptblöcken aktivieren

# ! Betrifft PowerShell 7:
# ! Im Gegensatz zu Linux oder macOS muss in Windows der Ereignisanbieter registriert sein,
# ! bevor Ereignisse in das Ereignisprotokoll geschrieben werden können.
# ! Führen Sie mit erhöhten Berechtigungen folgendes Script aus:
# ! siehe auch: Get-Help -Name about_logging_windows -ShowWindow
. "$PsHome\RegisterManifest.ps1" # * -Unregister
Get-WinEvent -ListProvider *powershell* | Select-Object -Property Name

# ! ... für Windows PowerShell und PowerShell 7 (64/32bit):
New-Item -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell" -Name "ScriptBlockLogging" -ItemType "Key" -Force | 
    New-ItemProperty -Name "EnableScriptBlockLogging" -Value "1" -PropertyType "DWord" -Force
# TODO Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShellCore\ScriptBlockLogging
# TODO Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging
# TODO Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\Windows\PowerShellCore\ScriptBlockLogging

# ? Windows-Log auswerten (ID 4104 in Microsoft-Windows-PowerShell/Operational bzw. PowerShellCore/Operational)

# ! ... für Windows PowerShell bzw. PowerShellCore/Operational:
Get-WinEvent -FilterHashtable @{LogName="Microsoft-Windows-PowerShell/Operational" ; Id="4104" } | 
    Select-Object -Property "RecordId", "ProcessId", "MachineName", "UserId", "TimeCreated", "Message" |
    Sort-Object -Property "TimeCreated" | 
    Select-Object -Last 5

# TODO da noch Fehlerhaft: Get-WinEvent -LogName "Windows PowerShell" -MaxEvents 5 | Where-Object Id -eq 4104 | Select-Object -exp Message

# !!! Um sensible Daten zu schützen sollten diese Log's verschlüsselt werden (s. Protected Event Logging) !!!
Get-Help -Name about_logging_windows -ShowWindow

# ! ScriptBlockLogging deaktivieren
Set-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"                 -Name "EnableScriptBlockLogging" -Value 0
Set-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\PowerShellCore\ScriptBlockLogging"             -Name "EnableScriptBlockLogging" -Value 0
Set-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging"     -Name "EnableScriptBlockLogging" -Value 0
Set-ItemProperty -Path "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Policies\Microsoft\Windows\PowerShellCore\ScriptBlockLogging" -Name "EnableScriptBlockLogging" -Value 0

#endregion

#region Eine PowerShell-Session einschränken (RestrictedLanguage incl. Allowed Cmdlets)

$allowedCommands = @()
$allowedCommands += "ForEach-Object"
$allowedCommands += "Get-ChildItem"
$allowedCommands += "Get-Command"
$allowedCommands += "Get-Member"
$allowedCommands += "Get-Module"
$allowedCommands += "Out-Default"
$allowedCommands += "Select-Object"
$allowedCommands += "Test-NetConnection"
$allowedCommands += "Where-Object"

# ! Keine Anwendungen und Skripte ab jetzt erlauben:
$ExecutionContext.SessionState.Applications.Clear()
$ExecutionContext.SessionState.Scripts.Clear()

# ! Nicht erlaubte Cmdlets unsichtbar machen:
Get-Command -CommandType "Cmdlet", "Function" | 
    Where-Object -FilterScript { $allowedCommands -NotContains $_.Name } |
    ForEach-Object -Process {
        $_.Visibility = "Private"
    }

$ExecutionContext.SessionState.LanguageMode = "RestrictedLanguage"

#endregion

#region Aktivieren des eingeschränkten PowerShell-Sprachmodi (Constrained Language Mode (CLM))

# ! Zu den wichtigsten Sicherheits­mechanismen gehört der 'Constrained Language Mode',
# ! der mehrere gefährliche PowerShell-Features deaktiviert und ver­hindert so deren Missbrauch.
Get-Help -Name About_Language_Modes -ShowWindow
Get-Help -Name about_ConstrainedLanguage -ShowWindow
Get-Help -Name about_FullLanguage -ShowWindow
Get-Help -Name about_NoLanguage -ShowWindow
Get-Help -Name about_RestrictedLanguage -ShowWindow
Get-Help -Name about_Session_Configurations -ShowWindow
Get-Help -Name about_Session_ConfigurationFiles -ShowWindow

# ? Den 'Constrained Language Mode' aktivieren:
$ExecutionContext.SessionState.LanguageMode = [System.Management.Automation.PSLanguageMode]::ConstrainedLanguage # *? Default => FullLanguage
# ! ACHTUNG: Ein anschließendes Zurückstellen in der aktuellen Sitzung ist nicht mehr möglich!

# ? Den 'Constrained Language Mode' DAUERHAFT aktivieren:
[Environment]::SetEnvironmentVariable("__PSLockDownPolicy", "4", "Machine") # 1 => FullLanguage
$env:__PSLockDownPolicy
# ! Die Folge ist, das jeder PowerShell-Host (Console, ISE, VSCode, etc.) im eingeschränkten Modus startet!

# ? Eine automatische Erkennung einer Ausführungsbeschränkung (Constrained Language Mode) aktivieren:
# * Seit PowerShell 5 wird automatisch erkannt ob in den 'Constrained Language Mode' gewechselt soll.
# * Die PowerShell legt dazu unter $env:TEMP temporär ein Script an und versucht dieses auszuführen.
# * Wird die PowerShell bei der Ausführung gehindert, startet sie im eingeschränkten Sprach­modus.
# * Dazu müssen Sie lediglich das Ausführen von Skripten im Ordner:
$env:TEMP
# * verbieten. Realisieren können Sie das per Softwareeinschränkung (GPO) unter:
# ! Computerkonfiguration / Windows-Einstellungen / Sicherheitseinstellungen / Richtlinien für Softwareeinschränkung / Zusätzliche Regeln
# * oder per AppLocker unter:
# ! Computerkonfiguration / Windows-Einstellungen / Sicherheitseinstellungen / Anwendungssteuerungsrichtlinien / AppLocker / Skriptregeln

#endregion

#region PowerShell-Remoting absichern

# ! siehe unter: Just Enough Administration (JEA)

#endregion

#region Admin-Aufgaben delegieren mit Just Enough Administration (JEA)

# TODO siehe Just Enough Administration (https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/overview?view=powershell-7)

<# ! Kurzbeschreibung
 
Just enough Administration (JEA => Gerade genug Administration) ist ein Feature in Windows 10 und Server 2016, mit der sich die
Systemverwaltung mittels Power­Shell granular dele­gieren lässt.
 
Nicht-Administrator erhalten unabhängig von der Gruppen­mit­glied­schaft über eine Remote-Session mittels JEA zugriff auf bestimmte
Cmdlets oder gar einzelne ihrer Para­meter.
 
Das damit realisierte Prinzip 'Least Privilege' sieht vor, nur noch die tatsächlich minimalen Rechte für bestimmte Aufgaben und
Dienste an administrative Mitarbeiter zu erteilen.
 
Die Architektur basiert immer auf JEA-Endpoints, mit denen sich der Bediener via Enter-PSSession verbindet und remote Befehle
mit höheren Rechten auf Systemen ausführen kann.
#>


#region 1. Voraussetzungen prüfen

# TODO siehe https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/prerequisites?view=powershell-7

# ? PowerShell-Version:
$PSVersionTable.PSVersion -ge [Version]"5.0.0.0"

# ? OS-Version:
Get-ComputerInfo | ForEach-Object { 
    if(($_.OsName -imatch "Windows 10" -and $_.WindowsVersion -ge 1607) -or $_.OsName -imatch "Windows Server 2016") 
    { $true } else { $false }
}

# ? PS Remoting aktiv:
try { Invoke-Command -ComputerName "localhost" -ScriptBlock { 1 } -ErrorAction Stop ; $true } catch { $false }

#endregion

#region 2. Vorbereitungen treffen

# ! Remoting auf Bediener-PC und Wartungs-Server aktivieren (beachte auch B09_Remoting.ps1):
Get-NetAdapter -Physical | Set-NetConnectionProfile -NetworkCategory "Private" -PassThru
Enable-PSRemoting -SkipNetworkProfileCheck -Force
Set-Item -Path "WSMan:\localhost\Client\TrustedHosts" -Value "*" -Force

# ! Auf dem Wartungs-Server Nicht-Admin-Benutzer und -Gruppe festlegen / erstellen die Verwaltungsaufgaben erledigen sollen:
$NoAdminUser = "TestHorst"
$MaintenanceGroup = "_Demo_NichtAdmins"
$NewUser = @{
    Name                     = $NoAdminUser
    AccountExpires           = (Get-Date).AddHours(24)
    Description              = "24h Demo-Account"
    Password                 = (Read-Host -Prompt "Passwort für $NoAdminUser" -AsSecureString)
    PasswordNeverExpires     = $true
    UserMayNotChangePassword = $true
}
New-LocalUser @NewUser
New-LocalGroup -Name $MaintenanceGroup -Description "JEA-Demo: Gruppe für nicht Admins"
Add-LocalGroupMember -Group $MaintenanceGroup -Member $NoAdminUser

#endregion

#region 3. JEA Module erstellen in dem später Role Capabilities gesucht werden

$ModuleName = "_FirmaAG_JEA"
$ModulePath = Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules\$ModuleName"
New-Item -ItemType Directory -Path $ModulePath 
New-Item -ItemType File -Path (Join-Path -Path $ModulePath -ChildPath "$ModuleName.psm1")
New-ModuleManifest -Path (Join-Path -Path $ModulePath -ChildPath "$ModuleName.psd1") -RootModule "$ModuleName"

#endregion

#region 4. Eine Rollenfunktionsdatei (PowerShell Role Capability File (.PSRC)) erstellen

# TODO siehe JEA-Rollenfunktionen (https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/role-capabilities?view=powershell-7#create-a-role-capability-file)

$RoleCapabilitiesPath = Join-Path -Path $ModulePath -ChildPath "RoleCapabilities"
New-Item -ItemType Directory -Path $RoleCapabilitiesPath
$MaintenanceRoleCapPath = Join-Path -Path $RoleCapabilitiesPath -ChildPath "Maintenance.psrc"
$MaintenanceRoleCapabilityCreationParams = @{
    Path                = $MaintenanceRoleCapPath
    Author              = "Attila Krick"
    CompanyName         = "attilakrick.com"
    ModulesToImport     = "Microsoft.PowerShell.Core"
    VisibleProviders    = "Registry"
    VisibleCmdlets      = "Out-Default", `
                          "Get-Command", `
                          "Get-ChildItem", `
                          "Out-String", `
                          @{ Name = "Restart-Computer"; Parameters = @{ Name = "ComputerName" ; ValidatePattern = "VDI\d+"         }}, `
                          @{ Name = 'Restart-Service' ; Parameters = @{ Name = 'Name'         ; ValidateSet     = 'Dns', 'Spooler' }}
    FunctionDefinitions = @{ Name = 'Get-UserInfo' ; ScriptBlock = {$PSSenderInfo}}
    VisibleExternalCommands = "C:\Windows\System32\WhoAmI.exe"
}
New-PSRoleCapabilityFile @MaintenanceRoleCapabilityCreationParams
Start-Process -FilePath $MaintenanceRoleCapPath

#endregion

#region 5. Eine Sitzungskonfigurationsdatei (PowerShell Session Configuration File (.PSSC)) erstellen

# TODO siehe JEA-Sitzungskonfigurationen (https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/session-configurations?view=powershell-7)

$SessionConfigurationsPath = Join-Path -Path $env:ProgramData -ChildPath "JEASessionConfigurations"
New-Item -ItemType Directory -Path $SessionConfigurationsPath 
$JEADemoSessionPath = Join-Path -Path $SessionConfigurationsPath -ChildPath "JEADemoSession.pssc"
$JEAConfigParams = @{
    Path                = $JEADemoSessionPath
    Author              = "Attila Krick"
    CompanyName         = "attilakrick.com"
    SessionType         = "Default" # ! PROBLEM: "RestrictedRemoteServer"
    RunAsVirtualAccount = $true
    TranscriptDirectory = Join-Path -Path $SessionConfigurationsPath -ChildPath "Transcripts"
    RoleDefinitions     = @{ $MaintenanceGroup = @{RoleCapabilities = 'Maintenance'}}
    Full                = $true
}
New-PSSessionConfigurationFile @JEAConfigParams
Test-PSSessionConfigurationFile -Path $JEADemoSessionPath
Start-Process -FilePath $JEADemoSessionPath

#endregion

#region 6. Die Sitzungskonfiguration registrieren

# TODO siehe Registrieren von JEA-Konfigurationen (https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/register-jea?view=powershell-7)

# ? Übersicht der registrierten Sitzungskonfigurationen:
Get-PSSessionConfiguration | Select-Object Name, Permission

$SessionConfigName = "JEA_Demo"
Register-PSSessionConfiguration -Name $SessionConfigName -Path $JEADemoSessionPath -Force
Restart-Service WinRM

#endregion

#region 7. JEA als Bediener/Verwalter verwenden

# TODO siehe Verwenden von JEA (https://docs.microsoft.com/de-de/powershell/scripting/learn/remoting/jea/using-jea?view=powershell-7)

Get-PSSessionCapability -ConfigurationName $SessionConfigName -Username "Domain\$noAdminUser" -Full

$cred = Get-Credential -UserName "$noAdminUser" -Message "Credential für localhost"

Enter-PSSession -ComputerName "localhost" -Credential $cred                               # ! Nicht erlaubt
Enter-PSSession -ComputerName "localhost" -ConfigurationName "JEA_Demo" -Credential $cred # * Erlaubt

C:\Windows\System32\WhoAmI.exe           # * Erlaubt
Get-ChildItem -PAth Hkcu:\               # * Erlaubt
Restart-Service -Name Spooler            # * Erlaubt
Restart-Service -Name AudioSrv           # ! Nicht erlaubt
Restart-Computer -ComputerName localhost # ! Nicht erlaubt
Restart-Computer -ComputerName VDI009    # * Erlaubt
Get-UserInfo                             # * Erlaubt
Get-Command                              # * Erlaubt
exit                                     # * Erlaubt

#endregion

#region 8. Konfiguration rückgängig machen

Unregister-PSSessionConfiguration -Name $SessionConfigName
Remove-Item -Path $sessionConfigurationsPath -Recurse -Force
Remove-Item -Path $modulePath -Recurse -Force
Remove-LocalUser -Name $noAdminUser
Remove-LocalGroup -Name $MaintenanceGroup
Set-Item -Path "WSMan:\localhost\Client\TrustedHosts" -Value ([String]::Empty) -Force
Disable-PSRemoting -Force
Get-NetAdapter -Physical | Set-NetConnectionProfile -NetworkCategory "Public"

#endregion

#endregion