22. April 2015

PowerShell Version: >2
Modules: none
New-ItemProperty "hklm:\SOFTWARE\Microsoft\..." -Name "testkey" -Value 1 -PropertyType "DWord"
-Name is the RegKey Name
-Value is the Value in hexadecimal
-PropertyType defines that we want a DWORD
20. April 2015

PowerShell Version: >2
Modules: none
Today I am documenting a little script I worked on that needed some workarounds and tweaks to function. Basically, we have a GPO that acts on shutdown (to save users a restart after hotfix installation) and should be applied for the whole domain. Some users in this domain use RDP and need these specific hotfixes so I had to find a way to apply a machine based GPO with a script to a group of users. Also, the RDC 8.1 hotfixes need to be applied in a specific order (KB2574819, KB2830477, KB2857650, KB2913751, KB3036965, KB2862019), which is why I can not work with foreach here.
#receive username from logon script
$user = Get-Content 'C:\tmp\$variables.txt' | select -index 0
$os = (Get-WMIObject Win32_OperatingSystem).name
$wusa = "C:\windows\system32\wusa.exe"
$userGroups = ([Security.Principal.WindowsIdentity]"$($user)").Groups.Translate([System.Security.Principal.NTAccount])
$LogPath = "c:\tmp\hotfixes.log"
$date = Get-Date -Format "dd.MM.yyyy hh:mm"
Start-Transcript -Path $LogPath
# Windows 7 Client hotfixes
if ($os -like "*Windows 7*")
{
# Remotedesktop hotfixes
if ($userGroups -like "*main_rdp_group")
{
"##########################################################"
"# $date"
"##########################################################"
"User: $user"
"Hostname: $env:COMPUTERNAME"
"##########################################################"
# https://support.microsoft.com/en-us/kb/2830477
# Remote Desktop Connection (RDC) 8.1
"Installing Windows6.1-KB2574819-v2-x64.msu..."
& $wusa \\path_to_ad_share\Windows6.1-KB2574819-v2-x64.msu /quiet /norestart
"Installing Windows6.1-KB2830477-x64.msu..."
& $wusa \\path_to_ad_share\Windows6.1-KB2830477-x64.msu /quiet /norestart
"Installing Windows6.1-KB2857650-x64.msu..."
& $wusa \\hpath_to_ad_share\Windows6.1-KB2857650-x64.msu /quiet /norestart
"Installing Windows6.1-KB2913751-x64.msu..."
& $wusa \\path_to_ad_share\Windows6.1-KB2913751-x64.msu /quiet /norestart
# https://support.microsoft.com/en-us/kb/3036965
# Printing preferences window appears behind a RemoteApp window in Windows
"Installing Windows6.1-KB3036965-x6.msu..."
& $wusa \\path_to_ad_share\Windows6.1-KB3036965-x64.msu /quiet /norestart
# https://support.microsoft.com/en-us/kb/2862019
# A RemoteApp application main window takes the focus after the applications windows are maximized in Windows
"Installing Windows6.1-KB2862019-x64.msu..."
& $wusa \\path_to_ad_share\Windows6.1-KB2862019-x64.msu /quiet /norestart
}
}
Stop-Transcript
As the computer based GPO would use the computername$ as the username when running, I had to find a way to get the actual username for my group-filter:
$user = Get-Content 'C:\tmp\$variables.txt' | select -index 0
I simply added some lines at the end of the existing logon script:
# check if c:\tmp exists...
$path_to_c_tmp = Test-Path c:\tmp
if ($path_to_c_tmp -eq $true)
{
# Gather variables for further scripts
$env:USERNAME | Out-File 'C:\tmp\$variables.txt'
$env:COMPUTERNAME | Out-File 'C:\tmp\$variables.txt'-Append
$PSVersionTable.PSVersion.Major | Out-File 'C:\tmp\$variables.txt'-Append
}
else
{
# if c:\tmp does not exist create it and continue with gathering variables for further scripts
"c:\tmp does not exist, creating...."
New-Item c:\tmp -type directory
# check if creation was successfull
if ($? -eq $true)
{
"Success!"
# Gather variables for further scripts
$env:USERNAME | Out-File 'C:\tmp\$variables.txt'
$env:COMPUTERNAME | Out-File 'C:\tmp\$variables.txt'-Append
$PSVersionTable.PSVersion.Major | Out-File 'C:\tmp\$variables.txt'-Append
}
else
{
"Something went wrong..."
}
}
In this example we would only use the $env:USERNAME but I added computername and psversion also for further projects. I can now call every line of the C:\tmp\$variables.txt file with | select -index number_of_line and use this to make the if ($userGroups -like “*main_rdp_group”) work. (If c:\tmp does not exist it will create it and gather the information again.)
I will continue working on this script to enable results in the logs and more filter options for other hotfixes. The goal here is to have one script to apply all hotfixes in the environment to the assigned target group.
17. April 2015
I pretty often filter through AD user groups and this way even works with PowerShell 1.0:
([Security.Principal.WindowsIdentity]"$($env:USERNAME)").Groups.Translate([System.Security.Principal.NTAccount])
PowerShell Version: 1
Modules: none
16. April 2015
If you need the name of the operation system for any kind of filter use this:
(Get-WMIObject Win32_OperatingSystem).name
Result:
Microsoft Windows 7 Enterprise |C:\Windows|\Device\Harddisk0\Partition1
Possible filter for any kind of script:
$os = (Get-WMIObject Win32_OperatingSystem).name
if ($os -like "*Windows 7*")
{
}
PowerShell Version: 4
Modules: none
If you want to get your major PowerShell version run the following:
$PSVersionTable.PSVersion.Major
Result:
4
PowerShell Version: 4
Modules: none
If you have installed the “ActiveDirectory Module for Windows PowerShell” you can pretty easily handle every AD operation with PowerShell, like removing deactivated accounts for example:
Check for disabled users:
$users = Get-ADUser -Filter * | where {($_.enabled -eq $false)}
You can either measure
echo "COUNT:"
($users | measure).Count
or echo these users.
$user.UserPrincipalName
If you know want to remove those:
foreach ($user in $users)
{
Remove-ADUser -Identity $user.SAMAccountName
$disabled = $user.UserPrincipalName
"$disabled disabled."
}
And the whole script:
$users = Get-ADUser -Filter * | where {($_.enabled -eq $false)}
echo "COUNT:"
($users | measure).Count
foreach ($user in $users)
{
Remove-ADUser -Identity $user.SAMAccountName
$disabled = $user.UserPrincipalName
"$disabled disabled."
}
Update:
Since PowerShell 4 you can achieve this in one line:
Search-ADAccount -AccountDisabled | where {$_.ObjectClass -eq 'user'} | Remove-ADUser
PowerShell Version: 4
Modules: ActiveDirectory Module for Windows PowerShell
If you want to remove Desktop items with PowerShell, this script will get the local Desktop folder from the registry and look for the item mentioned in line 5 to remove it (old shortcuts for example).
$desktopfolder = (Get-ItemProperty "hkcu:\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders").Desktop
$shortcuts = Get-Childitem $desktopfolder
foreach ($item in $shortcuts)
{
if ($item -match "example.lnk")
{
Remove-Item $item.fullname
echo "#########################################################################"
echo $date
echo "$item from User $env:USERNAME at $env:COMPUTERNAME in $desktopfolder removed."
}
}
PowerShell Version: 4
Modules: none
If the Test-Connection is just not enough you can also add a port check to your PowerShell scripts:
$ErrorActionPreference = 'SilentlyContinue' # we do not want to see any errors here
$hostip = "127.0.0.1"
$port = "4441"
Test-Connection $hostip -Count 2 | out-null
if ([int]$? -eq 0)
{
echo "$hostip is offline!"
}
else
{
echo "$hostip is online!"
$Socket = New-Object Net.Sockets.TcpClient
$Socket.Connect($hostip, $port)
$ErrorActionPreference = 'Continue'
if ($Socket.Connected)
{
echo "-> $port is open!"
$Socket.Close()
}
else
{
echo "-> $port is closed or filtered!"
}
}
#resetting the variable between iterations is necessary.
$Socket = $null
Line 14 to 28 is where the magic happens:
$hostip = "127.0.0.1"
$port = "4441"
$Socket = New-Object Net.Sockets.TcpClient
$Socket.Connect($hostip, $port)
$ErrorActionPreference = 'Continue'
if ($Socket.Connected)
{
echo "-> $port is open!"
$Socket.Close()
}
else
{
echo "-> $port is closed or filtered!"
}
}
#resetting the variable between iterations is necessary.
$Socket = $null
Result:
127.0.0.1 is online!
-> 4441 is open!
PowerShell Version: 4
Modules: none
This script will clear the common temp folders on Windows 7 client computers.
$date = Get-Date -Format "dd.mm.yy-HH:mm"
$win_temp_folder = "C:\Windows\Temp"
$outfile = "C:\tmp\clear_tmp.log"
$temp_folders = Get-Item C:\Users\*\AppData\Local\Temp
$os = (Get-WMIObject Win32_OperatingSystem).name
if ($os -like "*Windows 7*")
{
# CLEAR APPDATA LOCAL TEMP FOLDER PER USER
foreach ($temp_folder in $temp_folders)
{
$user_temp_files = Get-ChildItem -Path $temp_folder
$user_temp_files_count = ($user_temp_files | measure).Count
if ($user_temp_files_count -gt 0)
{
echo "#########################################################################" | Out-File $outfile -Append
echo "$date" | Out-File $outfile -Append
$user_temp_folder_fullname = $temp_folder.FullName
echo "Clearing $user_temp_folder_fullname" | Out-File $outfile -Append
Remove-Item $user_temp_folder_fullname\* -Recurse -Force
}
}
# CLEAR WINDOWS TEMP FOLDER
echo "Clearing $win_temp_folder" | Out-File $outfile -Append
Remove-Item $win_temp_folder\* -Recurse -Force
}
In line 4 we make sure to grab the \AppData\Local\Temp folder for every user on the machine and with line 7 we apply this to Windows 7 client PCs only. The script will write it’s activity in the $outfile.
I recommend applying this as a GPO start up script to make sure that none of the files in those folders are in use while the script runs: Computer-Configuration\Policies\Windows-Settings\Scripts\
PowerShell Version: 4
Modules: none
This script will let you silently monitor a PING connection to 3 hosts. We apply this via GPO as a login script and it will monitor the user’s connection to those hosts and write connection losses to the outfile shown in line 4.
If a connection loss to one of the main hosts occurs we added 2 subhosts to check and see if the connection to the local gateway and filesever is OK.
$ErrorActionPreference= 'silentlycontinue'
$date_header = Get-Date -Format ddMMyy
$outfile = "PATHTOYOURLOG_$($env:USERNAME)_$($env:COMPUTERNAME)_$($date_header).log"
$host1 = "YOURHOST"
$host2 = "YOURHOST"
$host3 = "YOURHOST"
$sub_host1 = "YOURSUBHOST"
$sub_host2 = "YOURSUBHOST"
Get-Date -Format "dd.MM.yy HH:mm:ss" | Out-File $outfile -Append
echo "Starting script..." | Out-File $outfile -Append
do
{
# HOST1
Test-Connection $host1 -Count 4 | out-null
if ([int]$? -eq 0)
{
$date = Get-Date -Format "HH:mm:ss"
echo "########################################################" | Out-File $outfile -Append
echo "$date Lost connection to $host1" | Out-File $outfile -Append
# SUB-HOST1
Test-Connection $sub_host1 -Count 1 | Out-File $outfile -Append
# SUB-HOST2
Test-Connection $sub_host2 -Count 1 | Out-File $outfile -Append
}
# HOST2
Test-Connection $host2 -Count 4 | out-null
if ([int]$? -eq 0)
{
$date = Get-Date -Format "HH:mm:ss"
echo "########################################################" | Out-File $outfile -Append
echo "$date Lost connection to Host $host2" | Out-File $outfile -Append
# SUB-HOST1
Test-Connection $sub_host1 -Count 1 | Out-File $outfile -Append
# SUB-HOST2
Test-Connection $sub_host2 -Count 1 | Out-File $outfile -Append
}
# HOST3
Test-Connection $host3 -Count 4 | out-null
if ([int]$? -eq 0)
{
$date = Get-Date -Format "HH:mm:ss"
echo "########################################################" | Out-File $outfile -Append
echo "$date Lost connection to $host3" | Out-File $outfile -Append
# SUB-HOST1
Test-Connection $sub_host1 -Count 1 | Out-File $outfile -Append
# SUB-HOST2
Test-Connection $sub_host2 -Count 1 | Out-File $outfile -Append
}
}
while ($asd = 1)
PowerShell Version: 4
Modules: none