Category Archives: PowerShell

Get path and name of process by port

This needs to run as an administrator to get paths for all processes

This one liner gets the path of the process that is using local port 61119


([System.Diagnostics.Process]::GetProcessById((Get-NetTCPConnection -localport 61119).owningprocess)).path

This will get you a list of process identifiers (PID) sorted by local port


Get-NetTCPConnection | select localport, owningprocess | sort localport

Finally get all the ports in use by a process


Get-NetTCPConnection -owningprocess 61980 | select localport

Know a port and wonder what other ports the process is using?


Get-NetTCPConnection -owningprocess([System.Diagnostics.Process]::GetProcessById((Get-NetTCPConnection -localport 61119).owningprocess).id) | select localport

Find something on your HD

I was trying to search for something on a large file system, but between circular references in symbolic links creating errors with long paths etc it was harder than I thought it should be.

Here is some quick powershell to do a recursive, case insensative search while skipping symbolic links, but it does pick up hidden files and folders and ones with strange characters in the names.


function Recurse($path) {
# $path.tolower()
$global:counter++
$global:counter

$files = Get-ChildItem -literal $path -file -force
foreach ($file in $files) {
#$file.fullname.ToLower()
if ($file.fullname.ToLower().contains($searchstring)) {
# write-host $file.fullname -foregroundcolor "magenta"
$FoundArray.add( $file.fullname )
}
}
$folders = Get-ChildItem -literal $path -directory -force
foreach ($folder in $folders) {
# $folder.fullname
if ($folder.fullname.ToLower().contains($searchstring)) {
# write-host $folder.fullname -foregroundcolor "magenta"
$FoundArray.add( $folder.fullname )
}
if ( (get-item -literal $folder.fullname -force).Attributes.ToString().Contains("ReparsePoint") -eq $false) {
Recurse $folder.fullname
}
}
}
cls
$global:counter=0
$searchstring="dropbox"
$searchstring=$searchstring.tolower()
$FoundArray = New-Object System.Collections.ArrayList
recurse "C:\Users"
$FoundArray

Can’t change password – The password does not meet the password policy requirements.

Ever have a user call and complain that they can’t change their password?

They get an error message “The password does not meet the password policy requirements.” but they promise that the new password is complex?

Any you try and get the same thing?

Could be a minimum password age policy issue…

 

 

This powershell will lookup your domain password policy and match it against a user and the last password change.

 

I am posting this mostly because I had a hard time retrieving the  domain policy parts.

$Domain.maxPwdAge.Value kept returning an object of System.__ComObject

It took a lot of searching to eventually find a way to get the actual value out.

I am sorry I didn’t save the links to give credit… At the same time anyone who can explain this to all of us I would sure like to understand why $lngMaxPwdAge = $Domain.ConvertLargeIntegerToInt64($MPA) works and $mpa.value does not…


$MPA = $Domain.maxPwdAge.Value

<em id="__mceDel">$lngMaxPwdAge = $Domain.ConvertLargeIntegerToInt64($MPA)</em>

 

 

Either way here is the script that will tell you the policy and when a password was last changed.

It will show an alert if the password is under the min or over the max password age.


cls

$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')

While (!$result)
{
$UserName = Read-Host 'Username to check : '
if (!$UserName)
{
Write-Host "No Username Entered"
exit
}
$searcher.Filter = "(&(objectClass=User)(samAccountName=" + $username + "))"
$result = $searcher.Findone()
}
# get domain password policy (max pw age)
$D = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$Domain = [ADSI]"LDAP://$D"
$MPA = $Domain.maxPwdAge.Value
$MinPA = $Domain.minPwdAge.Value
# Convert to Int64 ticks (100-nanosecond intervals).
$lngMaxPwdAge = $Domain.ConvertLargeIntegerToInt64($MPA)
$lngMinPwdAge = $Domain.ConvertLargeIntegerToInt64($MinPA)
$MinPwdLength = $Domain.minPwdLength
$PwdHistory = $Domain.pwdHistoryLength

# Convert to days.
$MaxPwdAge = -$lngMaxPwdAge/(600000000 * 1440)
$MinPwdAge = -$lngMinPwdAge/(600000000 * 1440)

$lngPwdLastSet =$result.Properties.pwdlastset
$pwdLastSet = [datetime]::FromFileTime($lngPwdLastSet[0])

Write-Host $result.Path
Write-Host $result.Properties.cn " " $result.Properties.userprincipalname
Write-Host "Password Last Set : " $pwdLastSet
Write-Host "Max Password Age : " $MaxPwdAge
Write-Host "Min Password Age : " $MinPwdAge
Write-Host "Password History : " $PwdHistory
Write-Host "Min Password Length : " $MinPwdLength
if ($pwdLastSet -ge (Get-Date).AddDays(-$MinPwdAge)){Write-Host -ForegroundColor Red "Password can not be changed - Min Age"}
if ($pwdLastSet -ge (Get-Date).AddDays($MaxPwdAge)){Write-Host -ForegroundColor Red "Password Expired"}

Enjoy.

 

Report on SIDHistory

This script will use PowerShell to check your domain for any users or groups that have a SID history, and will report the SID History, current SID and username to c:\UserSid.csv

You can use the current domain or specify something specific.

 


cls
$strFilter = "(&(|(objectCategory=User)(objectCategory=Group))(sidHistory=*))"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher

$objSearcher.SearchRoot = $objDomain
#$objSearcher.SearchRoot = "LDAP://dc=domainname, dc=com" #or manually override domain here
$objSearcher.PageSize = 100
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"

"OldSID,CurrentSid,OldName,NewName" | Out-File c:\UserSid.csv

$colProplist = "name" , "objectsid", "sidhistory"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)| Out-Null}

$colResults = $objSearcher.FindAll()

foreach ($objResult in $colResults)
{
$UserSid = New-Object System.Security.Principal.SecurityIdentifier($objResult.Properties.objectsid[0], 0)
foreach ($sidhistory in $objResult.Properties.sidhistory)
{
$OldUserSid = New-Object System.Security.Principal.SecurityIdentifier($sidhistory, 0)

$sid = new-object System.Security.Principal.SecurityIdentifier($usersid)
$user = $sid.Translate([System.Security.Principal.NTAccount])
if ($user)
{
$outstring = $OldUserSid.Value + "," + $UserSid.Value + "," + $objResult.Properties.name + "," + $user.value
}
else
{
$outstring = $OldUserSid.Value + "," + $UserSid.Value + "," + $objResult.Properties.name + ",Not Found"
}
$outstring | Out-File -append c:\UserSid.csv

}
}

List all the groups a user is a member of … Thank-You PowerShell

Let me start by saying that if you still cling to vbscript (as I did for so long) I have to say come to the dark side we have cookies. Seriously things are so much better once you get it figured out.

For whatever reason I started working with arrays in this script and learned a few cool things.

Remember how hard it was to actually remove an item from an array?
$array.remove(object)
and this is done using the value of the array member so you don’t even need to know it’s address!

recall having to write whole loops to bubble sort your array? another one liner now.

[Array]::Sort([array]$ArrayName)

And perhaps my favorite in this script, how to loop through an array and see if it contains a value, again a single line.

$array -contains "value"

The other thing I do a lot in this script is work with AD objects. Search for a user find all the groups they are a member of then bind each group and check its members etc etc. Lots of good examples of how to get a handle on an AD object. took me a long time to get it right but once you do it’s easy.

$Object= [ADSI]("LDAP://"+ $x)

where $x is the distinguished name of the object…. don’t know the DN? use the searcher

$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
$searcher.Filter = "(&(objectClass=User)(samAccountName=$SAMAccountName))"
$result = $searcher.Findone()

The only trick now that you have the name is actually getting the AD object to work with.

$user=$result.GetDirectoryEntry()

Starting to enjoy PowerShell yet?

# for more info s@blackops.ca
#The script will prompt you for a user logon name (no domain)
#If you click cancel or don’t enter a name the script will exit
#The script will continue to prompt you until it gets a valid username.
#The script will then load all the groups the user is a member of and go through all the nested groups reporting the hierarchy and noting any groups the use is a member of more than once.
#At the end it will output a single list sorted by group name to make it easier to find a specific group.

# This is the core function it takes an array of AD groups via distinguished name and a depth field to help with spacing in the output. For each group that is a member of a previous group it will call itself with the new array and depth +1
function GroupEnnumerate ([System.Collections.ArrayList]$InputArray, [int]$Depth)
{
$spacer=""
for ($i=1;$i -le $Depth; $i++) {$spacer=$spacer + "`t"}
#the two above lines add a tab char foe each level deep to keep the output formatted

While ($InputArray.count -gt 0) #repent until the input array is empty
{
#The next line is important to make sure we have not already processed a group.
#This prevents an endless loop if a group is a member of itself either directly or indirectly
if (!($expandedgrouparray -contains $InputArray[0]))
{
$GroupName = $InputArray[0]
$expandedgrouparray.add($InputArray[0])|Out-Null #add the new group to the array of processed groups
$Group = [ADSI]("LDAP://"+ $groupname) #lookup the group in AD
$expandedgrouparraynames.add($group.name.tostring())|Out-Null #add the group friendly name to an array for the output at the end
Write-Host $spacer($Group.Name) #display the group in the output
$TempGroupArray = New-Object System.Collections.ArrayList $null #blank and init the temp array of sub groups
foreach ($x in $group.memberof) # for each sub-group do the following
{
$member = [ADSI]("LDAP://"+ $x) #lookup the memberof  in AD

If ($member.SchemaClassName -eq "group") #we don't care if the member is not a group, just in case...
{
$TempGroupArray.add($x)|Out-Null #add the sub-group to a temp array to be used when calling the function for the next loop
}
}
if ($TempGroupArray.Count -gt 0) #if we found any sub-groups start checking them
{
[Array]::Sort([array]$TempGroupArray) # sort the array so he output is easier to read
GroupEnnumerate $TempGroupArray ($Depth+1) # call the function again with the temparray if sub-groups and add another tab to the output
$tempGroupArray=$null #blank out the temp array
}
$InputArray.remove($InputArray[0]) #as we process each group in the input array we need to remove it
}
else
{
#If we have already processed the group once then just report and remove it from the input array
write-Host -ForegroundColor Red $spacer([ADSI]("LDAP://"+ ($InputArray[0]))).name "- Already a member"
$InputArray.remove($InputArray[0])
}
}
}

cls
$StartGroupArray = New-Object System.Collections.ArrayList
$ExpandedGroupArray = New-Object System.Collections.ArrayList
$ExpandedGroupArrayNames = New-Object System.Collections.ArrayList
#I create the arrarys as above so I can use the .add .remove etc

$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]'')
$user=$null
$PromptMessage="Please enter the user logon name"

#the following do loop continues until you don't enter anything or you enter a good username
do
{
$SAMAccountName=$null
$SAMAccountName = Read-Host $PromptMessage

if ($SAMAccountName) #if there was ANY input in the box
{
$searcher.Filter = "(&(objectClass=User)(samAccountName=$SAMAccountName))"
$result = $searcher.Findone() # search AD for the requested user
if ($result)
{
$user=$result.GetDirectoryEntry()
# if we find a user in ID then get the AD object to work with later
}
else
{
# if we don't find a user update the message prompt so the user knows something happened
$PromptMessage= "User " + $samaccountname + " not found - Please enter the user logon name"
}
}
else
{
exit #if there was not ANY input in the box quit the whole script
}
}
while (!$user) # until we have a valid user object in AD keep pestering the operator

$groups = $user.memberof # get a list of all the groups that this user is a member of
foreach($group in $groups)
{
#for each group that they are a member of add the group DN to the array for processing
$StartGroupArray = $startGroupArray + $group
}
Write-Host "User: " $user.displayName " " $user.sAMAccountName " is a member of:" #this is the header of the output
[Array]::Sort([array]$startGroupArray)#fort the array so the output is easier to read
GroupEnnumerate $startGroupArray 1 #initiate the top-level group check
Write-Host # space in the output
Write-Host "Complete list sorted by group name" #second output header
$ExpandedGroupArraynames | Sort-Object #sorted list of all the groups processed that the user is a member of.
Write-Host # space in the output
Write-Host "Script Complete"

File System Audit and the WABAC machine – Now with set-acl

‘Twas a long time ago, longer now than it seems
In a place that perhaps you’ve seen in your dreams.
For the story that you are about to be told
Began with the technicians of old.

A very long time ago, as a junior tech I got a seemingly simple request.  “Tell me what user X has access to”
Quickly I looked up the groups and and provided the list, and got the response you already know. “We know that but what do those groups have access to?”

Of course this is a completely different question, so I set forth to write some code that would go through a file server and would tell me what the permissions where and at what level they changed. I purchased books and tried to call the API directly but was not able to create what I wanted and since nobody else could do it either the project just dropped off the radar.

Very recently I got another request. “We need to look through a directory structure to see where the “Domain Admins” group does not have full access. Users have been creating folders and adjusting permissions that we will need to fix without resetting everything in the tree.”

This time I started writing in PowerShell, I didn’t think I would say this but I wish PowerShell existed in the NT days. I didn’t need to fix anything just report, and within a couple of hours I had it.

 

The following script will go through directories and output 1 line for each folder or file it finds where the requested group does not have the access level defined UNLESS the file or folder is inheriting permissions from a higher level. The intent is that you should only need to make 1 change per line of output to correct the permissions, and even if there are thousands of files with incorrect permissions that inherit permission from a  parent.

You could have this fix things if you want using set-acl but I didn’t need to.

 

cls
# The intent of this script
# Loop recursively through files and folders starting at $target provided below)
# Look for the existance of an account or group in the ACL
# Check the access level for that user \ groupin the ACL
# Generate a report showing ONLY the places you would need to make a change
# to make sure the required account has the correct access on the entire tree.
# you could easily use set.acl to actually correct for issues (see note below) but I only needed a report.
#  File System Audit and the WABAC machine - www.blackops.ca - blog - for more info.

$target="\\Servername\sharename"
$GroupToCheck="Domain\User"
$AccessExpected="FullControl"

Function CheckAll {
# This function starts at the folder provided above
param([string]$rootfolder)
CheckACL $rootfolder
#Check the current folder ACL's
$items = Get-ChildItem -Path $rootfolder
If ($items)
# The above IF traps for a condition of a folder with no files in it.
{
foreach ($item in $items)
{
if ($item.Attributes -ne "Directory")
{
# for each file (note the -NE) check the ACL for the file
checkacl $item.fullName
}
}
foreach ($item in $items)
{
if ($item.Attributes -eq "Directory")
{
# for each directory (note the -EQ this time) Recurse back into this function with the directory as the new root folder
CheckAll $item.fullName
}
}
}
}

Function CheckACL {
param([string]$target)
$Check=$false
$inheritON=$false
$acl=Get-Acl $target # this reads the ACL
if ($acl.AreAccessRulesProtected)
# if AreAccessRulesProtected is TRUE then that means that a folder is NOT inheriting it's permissions
# if a folder is inheriting then it will NOT appear in the output
{
foreach ($ACLDetail in $acl.Access)
{
#This loops through each user \ access level and checks to see if access levels are explicit or inherited.
if ($acldetail.IsInherited) {$inheritON=$true} #inheritance check if inhereted file \ folder will NOT appear in report
if ($acldetail.IdentityReference -eq $GroupToCheck) #This checks each user for the one we are looking for (provided at the top of the script
{
if ($acldetail.FileSystemRights -eq $AccessExpected) #This checks to see if the permissions are correct
{
$Check = $true
}
else
{
#If the permissions are not correct output the full path\name and the current access level
Write-Host -NoNewline $Target
Write-Host -NoNewline "`t" #tab
Write-Host $acldetail.FileSystemRights
# If you want to fix the issue you would need a set_acl here and once more below
}
}
}
if (!$Check -and !$inheritON) #if the expected user is not found AND there is no inheritance set then log the file\folder
{
Write-Host -NoNewline $target
write-host -NoNewline "`t" #tab
write-host -NoNewline $GroupToCheck
write-host " Missing"

# If you want to fix the issue you would need a set_acl here and once more above

}
}
}

"begin"
checkall $target
"end"

And yes I am old enough to remember the WABAC Machine although not quite old enough to see the episodes as they aired for the first time.

 

 

Well it didn’t take long to go from report only to please fix it, so I have another script. If you want the script to add the required access for the defined group use the script below. I actually found set-acl harder to use than I expected and remember you see different statements for each condition

ADD to ACL for folder
Modify ACL for folder
ADD to ACL for file
Modify ACL for file

 

cls

# The intent of this script
# Loop recursively through files and folders starting at $target provided below)
# Look for the existance of an account or group in the ACL
# Check the access level for that user \ groupin the ACL
# Generate a report showing ONLY the places you would need to make a change
# to make sure the required account has the correct access on the entire tree.
# for more info s@blackops.ca
# THIS VERSION WILL CORRECT ISSUES. 

$target="C:\Temp"
$GroupToCheck="BUILTIN\Administrators"
$AccessExpected="FullControl"

#$DesiredPermission = $GroupToCheck,$AccessExpected,"ContainerInherit, ObjectInherit","NoPropagateInherit","Allow"
$person = [System.Security.Principal.NTAccount]"$GroupToCheck"
$access = [System.Security.AccessControl.FileSystemRights]"$AccessExpected"
$Folderinheritance = [System.Security.AccessControl.InheritanceFlags]“ContainerInherit, ObjectInherit”
$Fileinheritance = [System.Security.AccessControl.InheritanceFlags]“None”
$propagation = [System.Security.AccessControl.PropagationFlags]“NoPropagateInherit”
$type = [System.Security.AccessControl.AccessControlType]“Allow”
$FolderaccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($person,$access,$Folderinheritance,$propagation,$type)
$FileaccessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($person,$access,$type)

$AccessModification = New-Object system.security.AccessControl.AccessControlModification
$AccessModification.value__ = 2
$Modification = $False

Function CheckAll {
# This function starts at the folder provided above
param([string]$rootfolder)
CheckACL $rootfolder
#Check the current folder ACL's
$items = Get-ChildItem -Path $rootfolder
	If ($items)
	# The above IF traps for a condition of a folder with no files in it.
	{
			foreach ($item in $items)
			{
			if ($item.Attributes -ne "Directory")
		       {
			   		 # for each file (note the -NE) check the ACL for the file
		             checkacl $item.fullName
		       }
		 	}
			foreach ($item in $items)
			{
			if ($item.Attributes -eq "Directory")
		       {
			   		# for each directory (note the -EQ this time) Recurse back into this function with the directory as the new root folder
		             CheckAll $item.fullName
		       }
		 	}
		}
}

Function CheckACL {
param([string]$target)
$Check=$false
$inheritON=$false
$acl=Get-Acl $target # this reads the ACL
if ($acl.AreAccessRulesProtected)
# if AreAccessRulesProtected is TRUE then that means that a folder is NOT inheriting it's permissions
# if a folder is inheriting then it will NOT appear in the output
	{
	foreach ($ACLDetail in $acl.Access)
		{
		#This loops through each user \ access level and checks to see if access levels are explicit or inherited.
		if ($acldetail.IsInherited) {$inheritON=$true} #inheritance check if inhereted file \ folder will NOT appear in report
		if ($acldetail.IdentityReference -eq $GroupToCheck) #This checks each user for the one we are looking for (provided at the top of the script
			{
			if ($acldetail.FileSystemRights -eq $AccessExpected) #This checks to see if the permissions are correct
				{
				$Check = $true
				}
			else
				{
				#If the permissions are not correct output the full path\name and the current access level
				Write-Host -NoNewline $Target
				Write-Host -NoNewline "`t" #tab
				Write-Host  $acldetail.FileSystemRights
				If (Test-Path $target -pathtype container)
					{
					# If you want to fix the issue you would need a set_acl here and once more below
					$acl.ModifyAccessRule($AccessModification, $FolderaccessRule, [ref]$Modification) |Out-Null
					Set-Acl $target -AclObject $acl
					$Check = $true
					}
				Else
					{
					$acl.ModifyAccessRule($AccessModification, $FileaccessRule, [ref]$Modification) |Out-Null
					Set-Acl $target -AclObject $acl
					$Check = $true 

					}
				}
			}
		}
		if (!$Check -and !$inheritON) #if the expected user is not found AND there is no inheritance set then log the file\folder
			{
			Write-Host -NoNewline $target
			write-host -NoNewline "`t" #tab
			write-host -NoNewline $GroupToCheck
			write-host  " Missing"
			if (Test-Path $target -pathtype container)
				{
				$acl.AddAccessRule($FolderaccessRule)
				Set-Acl $target -AclObject $acl
				# If you want to fix the issue you would need a set_acl here and once more above
				}
			Else
				{
				$acl.AddAccessRule($FileaccessRule)
				Set-Acl $target -AclObject $acl

				}

			}
	}
}

"begin"
checkall $target
"end"