Tag Archives: Powershell

Import & Update Spamhaus DROP List in ISA/TMG

Along with their anti-spam Block Lists, Spamhaus also provide a DROP list, which is a list of stolen ‘hijacked’ netblocks and netblocks controlled entirely by criminals and professional spammers. As nothing good can come out of these ranges, it’s handy to block them outright at the firewall level, if that firewall happens to be TMG or ISA you can make use of the following script to keep the DROP list up-to-date.

The IP address conversion functions in this script are all courtesy of Chris Dent of www.indented.co.uk and were a godsend. To make use of this script you will need to create your Network object in ISA/TMG and update the network name in the following code to match $ipranges = $server.NetworkConfiguration.Networks.item("Spamhaus DROP List").IpRangeSet. You will also need to set $dropfile to match the location where you will be storing the downloaded DROP List, this needs to be retained so that updates can easily do a diff on the file without having to query ISA/TMG, which is much slower. Create a blank text file matching this filename & path before running the script for the first time.

<#
Copyright (c) 2013, Adam Beardwood
All rights reserved.
 
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: 
 
1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer. 
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution. 
 
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies, 
either expressed or implied, of the FreeBSD Project.
#>
 
#Grab Spamhaus DROP List and update TMG network sets based on changes
#IP Conversion Functions Courtesy of Chris Dent - http://www.indented.co.uk/index.php/2010/01/23/powershell-subnet-math/
#Adam Beardwood 26/03/2013
#v1.0 - Initial Release
#v1.1 - Considerable improvements to error checking
 
Function ConvertTo-DottedDecimalIP {
  <#
    .Synopsis
      Returns a dotted decimal IP address from either an unsigned 32-bit integer or a dotted binary string.
    .Description
      ConvertTo-DottedDecimalIP uses a regular expression match on the input string to convert to an IP address.
    .Parameter IPAddress
      A string representation of an IP address from either UInt32 or dotted binary.
  #>
 
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [String]$IPAddress
  )
 
  Process {
    Switch -RegEx ($IPAddress) {
      "([01]{8}\.){3}[01]{8}" {
        Return [String]::Join('.', $( $IPAddress.Split('.') | ForEach-Object { [Convert]::ToUInt32($_, 2) } ))
      }
      "\d" {
        $IPAddress = [UInt32]$IPAddress
        $DottedIP = $( For ($i = 3; $i -gt -1; $i--) {
          $Remainder = $IPAddress % [Math]::Pow(256, $i)
          ($IPAddress - $Remainder) / [Math]::Pow(256, $i)
          $IPAddress = $Remainder
         } )
 
        Return [String]::Join('.', $DottedIP)
      }
      default {
        Write-Error "Cannot convert this format"
      }
    }
  }
}
 
Function ConvertTo-DecimalIP {
  <#
    .Synopsis
      Converts a Decimal IP address into a 32-bit unsigned integer.
    .Description
      ConvertTo-DecimalIP takes a decimal IP, uses a shift-like operation on each octet and returns a single UInt32 value.
    .Parameter IPAddress
      An IP Address to convert.
  #>
 
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress
  )
 
  Process {
    $i = 3; $DecimalIP = 0;
    $IPAddress.GetAddressBytes() | ForEach-Object { $DecimalIP += $_ * [Math]::Pow(256, $i); $i-- }
 
    Return [UInt32]$DecimalIP
  }
}
 
Function Get-BroadcastAddress {
  <#
    .Synopsis
      Takes an IP address and subnet mask then calculates the broadcast address for the range.
    .Description
      Get-BroadcastAddress returns the broadcast address for a subnet by performing a bitwise AND 
      operation against the decimal forms of the IP address and inverted subnet mask. 
      Get-BroadcastAddress expects both the IP address and subnet mask in dotted decimal format.
    .Parameter IPAddress
      Any IP address within the network range.
    .Parameter SubnetMask
      The subnet mask for the network.
  #>
 
  [CmdLetBinding()]
  Param(
    [Parameter(Mandatory = $True, Position = 0, ValueFromPipeline = $True)]
    [Net.IPAddress]$IPAddress, 
 
    [Parameter(Mandatory = $True, Position = 1)]
    [Alias("Mask")]
    [Net.IPAddress]$SubnetMask
  )
 
  Process {
    Return ConvertTo-DottedDecimalIP $((ConvertTo-DecimalIP $IPAddress) -BOr `
      ((-BNot (ConvertTo-DecimalIP $SubnetMask)) -BAnd [UInt32]::MaxValue))
  }
}
 
#Create ISA COM Objects
$root = new-object -comObject "FPC.Root" -strict
#Get our array (well, there is only one in this case)
$server = $root.Arrays | Select-Object -first 1
 
#Get IPRangeSet for the Spamhaus DROP List network
$ipranges = $server.NetworkConfiguration.Networks.item("Spamhaus DROP List").IpRangeSet
$banned = $server.NetworkConfiguration.Networks.item("Banned Hosts").IpRangeSet
$dropfile = "c:\users\adam\desktop\drop.txt"
 
#If local copy of DROP file is missing, remove all addresses and start clean
if($(test-path $dropfile) -eq $false){
 
	#$ipranges.removeall()
 
	#Download DROP List from webserver
	$source = "http://www.spamhaus.org/drop/drop.txt"
	$destination = $dropfile
	$wc = New-Object System.Net.WebClient
	$wc.DownloadFile($source, $destination)
 
	$drop = ""
	$difference = Compare-Object $drop $(gc $dropfile)
 
}else{
 
	$drop = gc $dropfile
	if($drop -eq $null){$drop = ";"}
 
	#Download DROP List from webserver
	$source = "http://www.spamhaus.org/drop/drop.txt"
	$destination = $dropfile
	$wc = New-Object System.Net.WebClient
	$wc.DownloadFile($source, $destination)
 
	$difference = Compare-Object $drop $(gc $dropfile)
}
 
if($difference -ne $null){
 
	$difference
 
	foreach($diff in $difference){
		if ($diff.inputobject.startswith(";")){
		}else{
			$ip = $($diff.inputobject.split(" ; ")[0]).split("/")[0]
			$cidr = $($diff.inputobject.split(" ; ")[0]).split("/")[1]
			$mask = ConvertTo-DottedDecimalIP ([Convert]::ToUInt32($(("1" * $cidr).PadRight(32, "0")), 2))
			$bcast = Get-BroadcastAddress $ip $mask
 
			Switch ($diff.SideIndicator){
				"=>" {if(!$ipranges.isipinset($ip)){$ipranges.add($ip,$bcast)}elseif($banned.isipinset($ip)){write-host "IP Address Range $ip - $bcast already in Banned Hosts" -fore red}else{write-host "IP Address Range $ip - $bcast already in DROP List" -fore red}} #Not in local
				"<=" {if($ipranges.isipinset($ip)){$ipranges.remove($ip,$bcast)}else{write-host "IP Address Range $ip - $bcast not present in Network Object" -fore yellow}} #Not in remote
			}
		}
	}
 
}else{
	write-host "No Changes Since Last Update" -fore green
	}
 
$ipranges.save()
 
write-host "Update Complete" -fore green

Identifying Static DNS Configuration

It’s very easy to use something like Win32_NetworkAdapterConfiguration to get information from a remote machine as to whether or not it’s using DHCP on an interface, but it’s much harder to establish whether or not a client is using statically assigned DNS servers, but a DHCP assigned IP address.

This is the best way I’ve found to do it so far, but please let me know if you’re aware of a better one. Note that this function will return True if any interface that’s using DHCP has statically assigned DNS servers; if you want to get more specific you’ll have to start doing some interface detection and that’s a pain given the way they’re stored in the registry.

function checkstatic ($computer) {
	$rootkey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer)
	$key = $rootkey.OpenSubKey("System\CurrentControlSet\Services\TCPIP\Parameters\Interfaces")
	$subkeys = $key.getsubkeynames()
	foreach($subkey in $subkeys){
		$sub = $key.opensubkey($subkey, $true)
		$dhcp = $sub.getvalue("EnableDHCP")
		if($dhcp -eq 1){
			$nameserver = $sub.getvalue("Nameserver")
			if($nameserver -ne ""){
				return $true
			}
		}
	}
}

DHCP-Multitool

The following script is an amalgamation of several tools I’ve built up over the last couple of years to work with multiple DHCP scopes on servers. It has 6 basic modes of operation, as detailed below, which allow you to list all scopes on a server, enable all scopes on a server, disable all scopes on a server, change the lease time for all scopes on a server, change the offer delay for all scopes on a server or add reservations to one or more servers. You can use help .\DHCP-Tool.ps1 to get more information on the various switches, though it’s worth pointing out that leasetimes are in seconds while delaytimes are in milliseconds.

Hopefully the new Server 2012 powershell modules for DHCP solve a lot of these problems, but I haven’t had a chance to dig into them yet.

.\DHCP-Tool.ps1 -Server <String> [-show]
.\DHCP-Tool.ps1 -Server <String> [-enable]
.\DHCP-Tool.ps1 -Server <String> [-disable]
.\DHCP-Tool.ps1 -Server <String> [-lease] -leasetime <String>
.\DHCP-Tool.ps1 -Server <String> [-delay] -delaytime <Int32>
.\DHCP-Tool.ps1 -Server <String> [-reserve] -ipscope <String> -ipaddr <String> -mac <String> [-altservers <String>] [-name <String>]

################################################################################################
#This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;    #
#without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     #
#THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT      #
#WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS PROVIDE THE SOFTWARE "AS IS" WITHOUT   #
#WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE         #
#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK   #
#AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE      #
#DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT  #
#UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, BE       #
#LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES#
#ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS    #
#OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A    #
#FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER HAS BEEN     #
#ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.                                                   #
################################################################################################
 
<#
.SYNOPSIS
    DHCP Multitool
.DESCRIPTION
    Provides a variety of functions for making changes to multiple DHCP scopes and/or multiple DHCP servers
.PARAMETER Server
    The hostname or IP address of the DHCP server to work with
.PARAMETER Show
    Action: List current scope configuration
.PARAMETER Enable
	Action: Enable all scopes on server
.PARAMETER Disable
	Action: Disable all scopes on server
.PARAMETER Lease
	Action: Alters lease time on all scopes on server
.PARAMETER LeaseTime
	DHCP Lease time in seconds
.PARAMETER Delay
	Action: Alters offer delay on all scopes on server
.PARAMETER DelayTime
	Offer Delay time in miliseconds
.PARAMETER Reserve
	Action: Add reservation to server
.PARAMETER AltServer
	Additional servers to create reservation on
.PARAMETER IPScope
	Name of scope to work with
.PARAMETER IPAddr
	IP Address to reserve
.PARAMETER MAC
	MAC Address to associated reservation with
.PARAMETER Name
	Comment to add to reservation
.EXAMPLE
    C:\>dhcp-tool.ps1 -server 127.0.0.1 -enable
	Enables all scopes on server 127.0.0.1
.EXAMPLE
    C:\>dhcp-tool.ps1 -server 127.0.0.1 -delay -delaytime 1000
	Sets an offer delay of 1000ms on all scopes on server 127.0.0.1
.NOTES
    Author: Adam Beardwood
    Date: October 12, 2012
	v1.0: Initial Release
	v1.1: Added Better Error Handling and Command Feedback
#>
 
[CmdletBinding(DefaultParametersetName="showDHCP")]
Param( 
        [Parameter(Mandatory=$true,ValueFromPipeline=$true)][String]$Server,
 
		[Parameter(ParameterSetName='showDHCP')][switch]$show,
 
		[Parameter(ParameterSetName='enableDHCP')][switch]$enable,
 
		[Parameter(ParameterSetName='disableDHCP')][switch]$disable,
 
		[Parameter(ParameterSetName='leaseDHCP')][switch]$lease,
		[Parameter(ParameterSetName='leaseDHCP',Mandatory=$true,ValueFromPipeline=$true)][string]$leasetime,
 
		[Parameter(ParameterSetName='delayDHCP')][switch]$delay,
		[Parameter(ParameterSetName='delayDHCP',Mandatory=$true,ValueFromPipeline=$true)][ValidateRange(0,1000)][int]$delaytime,
 
		[Parameter(ParameterSetName='reserveDHCP')][switch]$reserve,
		[Parameter(ParameterSetName='reserveDHCP',Mandatory=$true)][string]$ipscope,
		[Parameter(ParameterSetName='reserveDHCP',Mandatory=$true)][string]$ipaddr,
		[Parameter(ParameterSetName='reserveDHCP',Mandatory=$true)][string]$mac,
		[Parameter(ParameterSetName='reserveDHCP',Mandatory=$false)][string]$altservers = $null,
		[Parameter(ParameterSetName='reserveDHCP',Mandatory=$false)][string]$name
)
 
function getDHCPScope($servername, $type) {
 
	$ips = @()
 
	$scopes = netsh dhcp server $servername show scope
 
	if($type -eq "show"){
		foreach($scope in $scopes){
			write-host $scope -foregroundcolor green
		}
	}else{
 
		$regex = [regex]"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}"
 
		foreach($scope in $scopes){
 
			$addr = $scope | foreach {$regex.Matches($_) | foreach {$_.Captures[0].Value}}
			if($addr -ne $null){
				$ips += $addr[0]
			}
		}
 
		return $ips
	}
}
 
function enableDHCPScope($servername, $ip){
	netsh dhcp server $servername scope $ip set state 1 | out-null
}
 
function disableDHCPScope($servername, $ip){
	netsh dhcp server $servername scope $ip set state 0 | out-null
}
 
function leaseDHCPScope($servername, $ip, $leasetime){
	netsh dhcp server $servername scope $ip set optionvalue 51 DWORD $leasetime | out-null
	$leaseinfo = netsh dhcp server $servername scope $ip show optionvalue
	foreach($info in $leaseinfo){if($info -match "OptionId : 51"){write-host "Scope $ip lease length: $($($leaseinfo[$([array]::IndexOf($leaseinfo, $info))+4]).split('=')[1]) seconds" -foregroundcolor green}}
}
 
function delayDHCPScope($servername, $ip, $leasetime){
	netsh dhcp server $server scope $ip set delayoffer $delaytime | out-null
	$delayinfo = netsh dhcp server $servername scope $ip show delayoffer
	foreach($info in $delayinfo){if($info -match "DelayOffer"){write-host $info -foregroundcolor green}}
}
 
function reserveDHCPScope($servername, $altservers, $ipscope, $ipaddr, $mac, $name){
 
	$servers = @($servername)
 
	foreach($alt in $altservers){
		if(($alt -ne "") -and ($alt -ne $null)){$servers += "\\$alt"}
	}
 
	write-host "Servers: $servers"
 
	foreach($server in $servers){
 
		$result = netsh dhcp server $server scope $ipscope
		if($result -match "The command needs a valid Scope IP Address"){
			write-host "Scope Does Not Exist!"
			exit 1
		}
 
		$result = netsh dhcp server $server scope $ipscope show clients
		if($result -match $ipaddr){
			write-host "IP Address Already Reserved!"
			exit 1
		}
 
		$regex = [regex]"([0-9A-Fa-f]{2}[:-]*){5}([0-9A-Fa-f]{2})"
		$result = $mac | foreach {$regex.Matches($_) | foreach {$_.Captures[0].Value}}
		if($result -eq $null){
			write-host "Invalid MAC Address!"
			exit 1
		}
 
		netsh dhcp server $server scope $ipscope add reservedip $ipaddr $mac $name
	}
 
}
 
if($(Get-Service -ComputerName $server -Name DHCPServer -ErrorAction SilentlyContinue) -eq $null){
	write-host "The specified server does not appear to be running DHCP"
	exit 1
}
 
$servername = "\\$server"
 
switch ($PsCmdlet.ParameterSetName){
	"showDHCP"{getDHCPScope $servername "show" }
	"enableDHCP"{foreach($ip in $(getDHCPScope $servername)){enableDHCPScope $servername $ip};getDHCPScope $servername "show"}
	"disableDHCP"{foreach($ip in $(getDHCPScope $servername)){disableDHCPScope $servername $ip};getDHCPScope $servername "show"}
	"leaseDHCP"{foreach($ip in $(getDHCPScope $servername)){leaseDHCPScope $servername $ip $leasetime}}
	"delayDHCP"{foreach($ip in $(getDHCPScope $servername)){delayDHCPScope $servername $ip $delaytime}}
	"reserveDHCP"{reserveDHCPScope $servername $altservers $ipscope $ipaddr $mac $name}
}

Getting Last Logon Times For Members of A Group – ADSI Edition

This handy little script will pull all of the users from the specified AD group and then grab the LastLogon time from each specified DC (or you could use [DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().DomainControllers to get all of them in the current domain) as well as grabbing the LastLogonTimeStamp for good measure (see my previous post for the caveats on that one). You can also specify which attribute you want to sort the results on; I recommend samaccountname because it’s usually the most useful.

Obviously it’s much quicker and simpler to do this with the ActiveDirectory cmdlets, but sometimes you’re stuck working with a bunch of 2003 DCs and have to make do with ADSI.

################################################################################################
#This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;    #
#without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     #
#THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT      #
#WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS PROVIDE THE SOFTWARE "AS IS" WITHOUT   #
#WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE         #
#IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK   #
#AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE      #
#DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT  #
#UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, BE       #
#LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES#
#ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS    #
#OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A    #
#FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER HAS BEEN     #
#ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.                                                   #
################################################################################################
 
#AD Group Last Logon Checker (ADSI Edition)
#Adam Beardwood 20/08/2012
#v1.0 - Initial Release
#v1.5 - Added Datagrid & CSV output
 
$dcs = ("DC1","DC2","DC3")
$group = "CN=Domain Admins,CN=Users,DC=domain,dc=local"
$basedn = "DC=domain,DC=local"
$sorton = "samaccountname"
$useroutput = @()
 
foreach($dc in $dcs){
	$Search = New-Object DirectoryServices.DirectorySearcher([ADSI]"LDAP://$dc/$basedn")
	$Search.Filter = "(&(memberof=$group)(objectclass=user))"
	$Search.Sort = New-Object System.DirectoryServices.SortOption($sorton,"Ascending")
	$SearchRes1 = $Search.Findall()
 
	foreach($result in $searchres1){
 
		$user = $result.Properties.samaccountname
		$Search.Filter = "(&(objectclass=user)(samaccountname=$user))"
		$SearchRes2 = $Search.Findall()
 
		New-Variable -Name "$user" -value (New-Object System.Object) -ErrorAction Silentlycontinue
		(Get-Variable "$user").value | Add-Member -type NoteProperty -name Name -value $user[0] -ErrorAction Silentlycontinue
		foreach($adc in $dcs){
			(Get-Variable "$user").value | Add-Member -type NoteProperty -name $adc -value "" -ErrorAction Silentlycontinue
		}
		(Get-Variable "$user").value| Add-Member -type NoteProperty -name LLTS -value "" -ErrorAction Silentlycontinue
 
		if($SearchRes2[0].Properties.lastlogon -ne $null){
			(Get-Variable "$user").value.$dc = ([DateTime]::FromFileTime([Int64]::Parse($SearchRes2[0].Properties.lastlogon)))	
		}
 
		if($SearchRes2[0].Properties.lastlogontimestamp -ne $null){
			(Get-Variable "$user").value.LLTS = ([DateTime]::FromFileTime([Int64]::Parse($SearchRes2[0].Properties.lastlogontimestamp)))
		}
	}
}
 
foreach($result in $searchres1){
	$user = $result.Properties.samaccountname
	$useroutput += (Get-Variable "$user").value
}
 
$useroutput | Sort-Object Name | Out-GridView -Title "Last Logon Times"
#$useroutput | Sort-Object Name | Export-CSV C:\logons.csv

Disable NTFS Permissions Inheritance

The following is the Powershell equivalent of unticking the box below for the folder “C:\temp”:

NTFS Permissions Inheritance

$acl = get-acl "C:\temp"
$acl.SetAccessRuleProtection($true,$true)
$acl | set-acl

Note that SetAccessRuleProtection takes two boolean arguments; the first turns inheritance on ($False) or off ($True) and the second determines whether the previously inherited permissions are retained ($True) or removed ($False).

Test RDP Connectivity with Powershell

Very simply returns True or False for a given list of hostnames or IP addresses depending on whether or not it can connect to TCP/3389 – a successful connection does not mean that you will be able to login, of course. If you’re running RDP on a non-standard port, you’ll need to adjust the script appropriately.

param(
     [parameter(Mandatory=$true,ValueFromPipeline=$true)][string[]]$computername
     )
 
$results = @()
 
foreach($name in $computername){
 
        $result = "" | select Name,RDP
        $result.name = $name
 
        try{
           $socket = New-Object Net.Sockets.TcpClient($name, 3389)
           if($socket -eq $null){
                 $result.RDP = $false
           }else{
                 $result.RDP = $true
                 $socket.close()
           }
        }
        catch{
                 $result.RDP = $false
        }
        $results += $result
}
 
return $results

Powershell One-Liners

These two are useful if you’re writing scripts which require certain modules (such as ActiveDirectory) or snapins (such as SQL, Exchange or VMware) to be present on the local system and you want to check for them before executing the bulk of the script.

Check if a given Module (In this instance “ActiveDirectory”) is present on a system:

if($(Get-Module -ListAvailable) -match "ActiveDirectory"){"Module Present"}else{"Module Not Present"}

Check if a given Snapin (In this instance “VMware.VimAutomation.Core”) is present on a system:

if($(Get-PSSnapin -Registered) -match "VMware.VimAutomation.Core"){"Snapin Present"}else{"Snapin Not Present"}

Some of the most common snapins are:

  • VMware PowerCLI: VMware.VimAutomation.Core
  • Exchange 2007: Microsoft.Exchange.Management.Powershell.Admin
  • Exchange 2010: Microsoft.Exchange.Management.Powershell.E2010
  • SQL: SqlServerCmdletSnapin100 & SqlServerProviderSnapin100

Changing Printer Drivers En Masse

If you need to change the drivers for a large number of printers, such as on a print server, then you can use the following Powershell to do it. Set $driver to the name of the driver you wish to set and $pattern to match for the printers you wish to affect (so you don’t change the driver on printers you don’t want to).

Note: This script will run pretty quickly, but depending on the number of printers it may take upwards of 10 minutes for Windows to do all the background processing associated with the driver changes. Keep an eye out for a bunch of rundll32.exe processes which will spawn; once they close themselves down the changes should be complete.

$driver = "HP Universal Printing PCL 6 (v5.4)"
$pattern = "HP"
 
$printers = gwmi win32_printer
 
foreach($printer in $printers){
        $name = $printer.name
        if($name -match $pattern){
                & rundll32 printui.dll PrintUIEntry /Xs /n $name DriverName $driver
        }
}

Am I an Administrator?

Powershell. Returns True or False. Simple.

(New-Object System.Security.Principal.WindowsPrincipal([System.Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)

Decode UserAccountControl Values With Powershell

One of the most annoying things when working with Powershell and AD accounts is the UserAccountControl value. This value is what determines settings such as whether or not the account is locked out, disabled, requires a smartcard for authentication, uses reversible encryption for its password, etc. The default is 512 (NORMAL_ACCOUNT) but there are all kinds of weird and wonderful combinations that can turn up depending on how the account is configured and when you’re trying to (for example) find all the accounts that are set to USE_DES_KEY_ONLY then having so many different possible values (any number that could have 2097152 as part of its makeup) makes it a pain to work out.

Thankfully, someone has solved this problem in a relatively simple manner; I first came across it here: http://bsonposh.com/archives/288 and it is listed in all its glory below. It takes a single argument, which is the UAC value for an account and will return a list of all the flags that apply to that value. So, for example, plugging in a value of 66048 will give an output of:
NORMAL_ACCOUNT
DONT_EXPIRE_PASSWORD
.

param
([int]$value)
$flags = @("","ACCOUNTDISABLE","", "HOMEDIR_REQUIRED",
"LOCKOUT", "PASSWD_NOTREQD","PASSWD_CANT_CHANGE", "ENCRYPTED_TEXT_PWD_ALLOWED",
"TEMP_DUPLICATE_ACCOUNT", "NORMAL_ACCOUNT", "","INTERDOMAIN_TRUST_ACCOUNT", "WORKSTATION_TRUST_ACCOUNT",
"SERVER_TRUST_ACCOUNT", "", "", "DONT_EXPIRE_PASSWORD", "MNS_LOGON_ACCOUNT", "SMARTCARD_REQUIRED",
"TRUSTED_FOR_DELEGATION", "NOT_DELEGATED","USE_DES_KEY_ONLY", "DONT_REQ_PREAUTH",
"PASSWORD_EXPIRED", "TRUSTED_TO_AUTH_FOR_DELEGATION")
1..($flags.length) | ? {$value -band [math]::Pow(2,$_)} | % { $flags[$_] }